Coverage for archdocs/features/sqlalchemy/parser.py: 100%
17 statements
« prev ^ index » next coverage.py v7.15.3, created at 2026-08-03 00:00 +0000
« prev ^ index » next coverage.py v7.15.3, created at 2026-08-03 00:00 +0000
1import re as py_re
2import typing
4from archdocs import prefilter, settings
5from archdocs.features.sqlalchemy.const import SQLAlchemyFeatures
8_ASYNC_ENGINE_PATTERN: typing.Final = py_re.compile(r"sqlalchemy\.ext\.asyncio", settings.TYPICAL_RE_FLAGS)
9_POOLING_PATTERN: typing.Final = py_re.compile(r"create_engine\(.+pool_", settings.TYPICAL_RE_FLAGS)
10_MULTIPLE_HOSTS_PATTERN: typing.Final = py_re.compile(
11 r"create_engine\([^)]*'(?:postgresql|mysql|mariadb|oracle|mssql)\+[^']*://[^/]+,[^/]+/",
12 settings.TYPICAL_RE_FLAGS,
13)
14_TARGET_SESSION_ATTRS_PATTERN: typing.Final = py_re.compile(
15 r"target_session_attrs\s*=\s*['\"](\w+)['\"]",
16 settings.TYPICAL_RE_FLAGS,
17)
18_DB_TYPE_PATTERN: typing.Final = py_re.compile(
19 r"['\"](postgresql(?:\+[^'\"]*)?|mysql(?:\+[^'\"]*)?|sqlite(?:\+[^'\"]*)?|oracle(?:\+[^'\"]*)?|"
20 r"mssql(?:\+[^'\"]*)?|mariadb(?:\+[^'\"]*)?|cockroachdb(?:\+[^'\"]*)?)['\"]",
21 settings.TYPICAL_RE_FLAGS,
22)
23# One union of literals for the whole feature: an engine is found not only by the package
24# name but by a bare DSN in a string, so the database schemes of `_DB_TYPE_PATTERN` belong here too.
25_SQLALCHEMY_LITERALS: typing.Final = (
26 "sqlalchemy",
27 "create_engine",
28 "target_session_attrs",
29 "postgresql",
30 "mysql",
31 "sqlite",
32 "oracle",
33 "mssql",
34 "mariadb",
35 "cockroachdb",
36)
37_EMPTY_FEATURES: typing.Final = SQLAlchemyFeatures(
38 async_used=False,
39 pooling_used=False,
40 multiple_hosts=False,
41 target_session_attrs="",
42 database_type="",
43)
46def find_sqlalchemy_features(raw_source: str) -> SQLAlchemyFeatures:
47 if not prefilter.contains_any_literal(raw_source.lower(), _SQLALCHEMY_LITERALS):
48 return _EMPTY_FEATURES
49 target_session_attrs_match: typing.Final = _TARGET_SESSION_ATTRS_PATTERN.search(raw_source)
50 database_type_match: typing.Final = _DB_TYPE_PATTERN.search(raw_source)
51 return SQLAlchemyFeatures(
52 async_used=_ASYNC_ENGINE_PATTERN.search(raw_source) is not None,
53 pooling_used=_POOLING_PATTERN.search(raw_source) is not None,
54 multiple_hosts=_MULTIPLE_HOSTS_PATTERN.search(raw_source) is not None,
55 target_session_attrs=target_session_attrs_match.group(1) if target_session_attrs_match else "",
56 database_type=database_type_match.group(1) if database_type_match else "",
57 )