iker-python-common 1.0.44__tar.gz → 1.0.45__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/PKG-INFO +1 -1
  2. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/argutils.py +67 -1
  3. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/csv.py +52 -0
  4. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/dbutils.py +97 -13
  5. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/dtutils.py +109 -49
  6. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/funcutils.py +53 -0
  7. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/jsonutils.py +108 -0
  8. iker_python_common-1.0.45/src/iker/common/utils/logger.py +117 -0
  9. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/numutils.py +48 -0
  10. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/randutils.py +146 -1
  11. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/retry.py +85 -21
  12. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/s3utils.py +63 -63
  13. iker_python_common-1.0.45/src/iker/common/utils/sequtils.py +703 -0
  14. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/shutils.py +48 -51
  15. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/span.py +15 -15
  16. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/typeutils.py +60 -16
  17. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker_python_common.egg-info/PKG-INFO +1 -1
  18. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/sequtils_test.py +65 -28
  19. iker_python_common-1.0.44/src/iker/common/utils/logger.py +0 -64
  20. iker_python_common-1.0.44/src/iker/common/utils/sequtils.py +0 -546
  21. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/.editorconfig +0 -0
  22. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/.github/workflows/pr.yml +0 -0
  23. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/.github/workflows/push.yml +0 -0
  24. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/.gitignore +0 -0
  25. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/MANIFEST.in +0 -0
  26. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/README.md +0 -0
  27. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/VERSION +0 -0
  28. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/pyproject.toml +0 -0
  29. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/config/config.cfg +0 -0
  30. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/csv/data.csv +0 -0
  31. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/csv/data.tsv +0 -0
  32. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/s3utils/dir.baz/file.bar.baz +0 -0
  33. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/s3utils/dir.baz/file.foo.bar +0 -0
  34. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/s3utils/dir.baz/file.foo.baz +0 -0
  35. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/s3utils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
  36. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
  37. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
  38. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
  39. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/s3utils/dir.foo/file.bar +0 -0
  40. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/s3utils/dir.foo/file.baz +0 -0
  41. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/s3utils/dir.foo/file.foo +0 -0
  42. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/shutils/dir.baz/file.bar.baz +0 -0
  43. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/shutils/dir.baz/file.foo.bar +0 -0
  44. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/shutils/dir.baz/file.foo.baz +0 -0
  45. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/shutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
  46. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
  47. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
  48. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
  49. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/shutils/dir.foo/file.bar +0 -0
  50. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/shutils/dir.foo/file.baz +0 -0
  51. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/resources/unittest/shutils/dir.foo/file.foo +0 -0
  52. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/setup.cfg +0 -0
  53. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/setup.py +0 -0
  54. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/__init__.py +0 -0
  55. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/__init__.py +0 -0
  56. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/config.py +0 -0
  57. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/dockerutils.py +0 -0
  58. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/strutils.py +0 -0
  59. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker/common/utils/testutils.py +0 -0
  60. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker_python_common.egg-info/SOURCES.txt +0 -0
  61. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker_python_common.egg-info/dependency_links.txt +0 -0
  62. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker_python_common.egg-info/not-zip-safe +0 -0
  63. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker_python_common.egg-info/requires.txt +0 -0
  64. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/src/iker_python_common.egg-info/top_level.txt +0 -0
  65. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_test.py +0 -0
  66. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/__init__.py +0 -0
  67. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/argutils_test.py +0 -0
  68. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/config_test.py +0 -0
  69. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/csv_test.py +0 -0
  70. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/dbutils_test.py +0 -0
  71. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/dockerutils_test.py +0 -0
  72. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/dtutils_test.py +0 -0
  73. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/funcutils_test.py +0 -0
  74. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/jsonutils_test.py +0 -0
  75. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/logger_test.py +0 -0
  76. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/numutils_test.py +0 -0
  77. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/randutils_test.py +0 -0
  78. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/retry_test.py +0 -0
  79. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/s3utils_test.py +0 -0
  80. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/shutils_test.py +0 -0
  81. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/span_test.py +0 -0
  82. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/strutils_test.py +0 -0
  83. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/testutils_test.py +0 -0
  84. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/common/utils/typeutils_test.py +0 -0
  85. {iker_python_common-1.0.44 → iker_python_common-1.0.45}/test/iker_tests/docker_fixtures.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iker-python-common
3
- Version: 1.0.44
3
+ Version: 1.0.45
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.11
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -19,6 +19,14 @@ import sys
19
19
 
20
20
 
21
21
  class ParserTreeNode(object):
22
+ """
23
+ Represents a node in the parser tree, holding a command, its parser, and any child nodes. Each node may have
24
+ subparsers and a list of child nodes representing subcommands.
25
+
26
+ :param command: The command string for this node.
27
+ :param parser: The ``ArgumentParser`` associated with this node.
28
+ """
29
+
22
30
  def __init__(self, command: str, parser: argparse.ArgumentParser):
23
31
  self.command = command
24
32
  self.parser = parser
@@ -32,6 +40,16 @@ def construct_parser_tree(
32
40
  command_key_prefix: str,
33
41
  **kwargs,
34
42
  ) -> list[ParserTreeNode]:
43
+ """
44
+ Constructs a parser tree by traversing or creating nodes for each command in the command chain. Returns the path
45
+ from the ``root_node`` to the last node in the chain.
46
+
47
+ :param root_node: The root node of the parser tree.
48
+ :param command_chain: A list of command strings representing the path.
49
+ :param command_key_prefix: Prefix for command keys in the parser.
50
+ :param kwargs: Additional keyword arguments for parser creation.
51
+ :return: A list of ``ParserTreeNode`` objects representing the path from root to the last command.
52
+ """
35
53
  node_path = [root_node]
36
54
  if len(command_chain) == 0:
37
55
  return node_path
@@ -58,16 +76,37 @@ def construct_parser_tree(
58
76
 
59
77
 
60
78
  class ParserTree(object):
79
+ """
80
+ Represents a tree structure for managing ``argparse`` parsers and subcommands. Provides methods to add subcommand
81
+ parsers and parse arguments, returning the command chain and parsed namespace.
82
+
83
+ :param root_parser: The root ``ArgumentParser``.
84
+ :param command_key_prefix: Prefix for command keys in the parser tree.
85
+ """
86
+
61
87
  def __init__(self, root_parser: argparse.ArgumentParser, command_key_prefix: str = "command"):
62
88
  self.root_node = ParserTreeNode("", root_parser)
63
89
  self.command_key_prefix = command_key_prefix
64
90
 
65
91
  def add_subcommand_parser(self, command_chain: list[str], **kwargs) -> argparse.ArgumentParser:
92
+ """
93
+ Adds a subcommand parser for the specified command chain, creating intermediate nodes as needed.
94
+
95
+ :param command_chain: A list of command strings representing the subcommand path.
96
+ :param kwargs: Additional keyword arguments for parser creation.
97
+ :return: The ``ArgumentParser`` for the last command in the chain.
98
+ """
66
99
  *_, last_node = construct_parser_tree(self.root_node, command_chain, self.command_key_prefix, **kwargs)
67
100
  return last_node.parser
68
101
 
69
102
  def parse_args(self, args: list[str] | None = None) -> tuple[list[str], argparse.Namespace]:
70
- # Before Python 3.12 the `exit_on_error` attribute does not take effect properly
103
+ """
104
+ Parses the provided argument list, returning the command chain and the parsed namespace.
105
+
106
+ :param args: The list of arguments to parse. If ``None``, parses ``sys.argv``.
107
+ :return: A tuple containing the list of command strings and the parsed ``Namespace``.
108
+ """
109
+ # Before Python 3.12 the ``exit_on_error`` attribute does not take effect properly
71
110
  # if unknown arguments encountered. We have to employ this workaround
72
111
  if sys.version_info < (3, 12):
73
112
  if self.root_node.parser.exit_on_error:
@@ -92,6 +131,19 @@ class ParserTree(object):
92
131
 
93
132
  @dataclasses.dataclass(frozen=True)
94
133
  class ArgParseSpec(object):
134
+ """
135
+ Specification for an argument to be added to an ``ArgumentParser``. Allows detailed configuration of argument
136
+ properties such as ``flag``, ``name``, ``type``, ``action``, ``default``, ``choices``, and help text.
137
+
138
+ :param flag: The optional flag for the argument (e.g., '-f').
139
+ :param name: The name of the argument (e.g., '--file').
140
+ :param action: The action to be taken by the argument parser.
141
+ :param default: The default value for the argument.
142
+ :param type: The type of the argument value.
143
+ :param choices: A list of valid choices for the argument.
144
+ :param required: Whether the argument is required.
145
+ :param help: The help text for the argument.
146
+ """
95
147
  flag: str | None = None
96
148
  name: str | None = None
97
149
  action: str | None = None
@@ -102,6 +154,12 @@ class ArgParseSpec(object):
102
154
  help: str | None = None
103
155
 
104
156
  def make_kwargs(self) -> dict[str, Any]:
157
+ """
158
+ Constructs a dictionary of keyword arguments for ``ArgumentParser.add_argument``, omitting any that are
159
+ ``None``.
160
+
161
+ :return: A dictionary of argument properties suitable for ``ArgumentParser.add_argument``.
162
+ """
105
163
  kwargs = dict(
106
164
  action=self.action,
107
165
  default=self.default,
@@ -118,6 +176,14 @@ argparse_spec = ArgParseSpec
118
176
 
119
177
 
120
178
  def make_argparse(func, parser: argparse.ArgumentParser = None) -> argparse.ArgumentParser:
179
+ """
180
+ Automatically generates an ``ArgumentParser`` for the given function by inspecting its signature and parameter
181
+ annotations. Supports ``ArgParseSpec`` for detailed argument configuration.
182
+
183
+ :param func: The function whose parameters will be used to generate arguments.
184
+ :param parser: An optional ``ArgumentParser`` to add arguments to. If ``None``, a new parser is created.
185
+ :return: The ``ArgumentParser`` with arguments added based on the function signature.
186
+ """
121
187
  if parser is None:
122
188
  parser = argparse.ArgumentParser()
123
189
 
@@ -11,6 +11,16 @@ __all__ = [
11
11
 
12
12
 
13
13
  class CSVColumn(object):
14
+ """
15
+ Represents a column in a CSV schema, including its name, loader function, dumper function, and the string used for
16
+ null values.
17
+
18
+ :param name: The name of the column.
19
+ :param loader: A function to convert a string to the column's value type.
20
+ :param dumper: A function to convert the column's value to a string.
21
+ :param null_str: The string representation of a null value in this column.
22
+ """
23
+
14
24
  def __init__(
15
25
  self,
16
26
  name: str,
@@ -25,6 +35,15 @@ class CSVColumn(object):
25
35
 
26
36
 
27
37
  class CSVView(object):
38
+ """
39
+ Represents a view for reading and writing CSV data according to a schema. Supports loading and dumping lines or
40
+ files, with options for headers and dictionary or list output.
41
+
42
+ :param schema: The sequence of ``CSVColumn`` objects defining the schema.
43
+ :param row_delim: The delimiter for rows (default is ``'\n'``).
44
+ :param col_delim: The delimiter for columns (default is ``','``).
45
+ """
46
+
28
47
  def __init__(self, schema: Sequence[CSVColumn], *, row_delim: str = "\n", col_delim: str = ","):
29
48
  self.schema = schema
30
49
  self.row_delim = row_delim
@@ -36,6 +55,15 @@ class CSVView(object):
36
55
  has_header: bool = True,
37
56
  ret_dict: bool = False,
38
57
  ) -> Generator[list[Any] | dict[str, Any], None, None]:
58
+ """
59
+ Loads CSV data from an iterable of lines, optionally using the first line as a header. Returns each row as a
60
+ list or dictionary, depending on ``ret_dict``.
61
+
62
+ :param lines: An iterable of CSV lines.
63
+ :param has_header: Whether the first line is a header.
64
+ :param ret_dict: Whether to return rows as dictionaries (``True``) or lists (``False``).
65
+ :return: A generator yielding each row as a list or dictionary.
66
+ """
39
67
  rows_iter = iter(lines)
40
68
  if has_header:
41
69
  header_row = next(rows_iter)
@@ -59,6 +87,13 @@ class CSVView(object):
59
87
  data: Iterable[Sequence[Any] | Mapping[str, Any]],
60
88
  has_header: bool = True,
61
89
  ) -> Generator[str, None, None]:
90
+ """
91
+ Dumps data to CSV lines according to the schema, optionally including a header row.
92
+
93
+ :param data: An iterable of rows, each as a sequence or mapping.
94
+ :param has_header: Whether to include a header row.
95
+ :return: A generator yielding CSV lines as strings.
96
+ """
62
97
  if has_header:
63
98
  yield self.col_delim.join(c.name for c in self.schema)
64
99
  for cols in data:
@@ -78,6 +113,15 @@ class CSVView(object):
78
113
  ret_dict: bool = False,
79
114
  **kwargs,
80
115
  ) -> Generator[list[Any] | dict[str, Any], None, None]:
116
+ """
117
+ Loads CSV data from a file, splitting by row delimiter and using the ``schema`` for parsing.
118
+
119
+ :param file_path: The path to the CSV file.
120
+ :param has_header: Whether the first line is a header.
121
+ :param ret_dict: Whether to return rows as dictionaries (``True``) or lists (``False``).
122
+ :param kwargs: Additional keyword arguments for file opening.
123
+ :return: A generator yielding each row as a list or dictionary.
124
+ """
81
125
  with open(file_path, mode="r", **kwargs) as fh:
82
126
  lines = fh.read().split(self.row_delim)
83
127
  yield from self.load_lines(lines, has_header, ret_dict)
@@ -89,6 +133,14 @@ class CSVView(object):
89
133
  has_header: bool = True,
90
134
  **kwargs,
91
135
  ) -> None:
136
+ """
137
+ Dumps data to a CSV file according to the ``schema``, optionally including a header row.
138
+
139
+ :param data: An iterable of rows, each as a sequence or mapping.
140
+ :param file_path: The path to the output CSV file.
141
+ :param has_header: Whether to include a header row.
142
+ :param kwargs: Additional keyword arguments for file opening.
143
+ """
92
144
  with open(file_path, mode="w", **kwargs) as fh:
93
145
  for line in self.dump_lines(data, has_header):
94
146
  fh.write(line)
@@ -25,7 +25,17 @@ __all__ = [
25
25
 
26
26
  class ConnectionMaker(object):
27
27
  """
28
- Provides utilities that make it easier to establish database connections and sessions
28
+ Provides utilities to simplify establishing database connections and sessions, including connection string
29
+ construction, engine and session creation, and model management.
30
+
31
+ :param driver: The database driver string (e.g., ``mysql+pymysql``).
32
+ :param host: The database host.
33
+ :param port: The database port.
34
+ :param user: The database user.
35
+ :param password: The database password.
36
+ :param database: The database name.
37
+ :param engine_opts: Optional dictionary of SQLAlchemy engine options.
38
+ :param session_opts: Optional dictionary of SQLAlchemy session options.
29
39
  """
30
40
 
31
41
  class Drivers:
@@ -59,6 +69,15 @@ class ConnectionMaker(object):
59
69
  engine_opts: dict[str, Any] | None = None,
60
70
  session_opts: dict[str, Any] | None = None,
61
71
  ) -> "ConnectionMaker":
72
+ """
73
+ Creates a ``ConnectionMaker`` instance from a URL string or ``ParseResult``, extracting connection parameters.
74
+
75
+ :param driver: The database driver string.
76
+ :param url: The database URL as a string or ``ParseResult``.
77
+ :param engine_opts: Optional dictionary of SQLAlchemy engine options.
78
+ :param session_opts: Optional dictionary of SQLAlchemy session options.
79
+ :return: A ``ConnectionMaker`` instance.
80
+ """
62
81
  if isinstance(url, str):
63
82
  return ConnectionMaker.from_url(driver, urllib.parse.urlparse(url), engine_opts, session_opts)
64
83
  if isinstance(url, urllib.parse.ParseResult):
@@ -74,6 +93,11 @@ class ConnectionMaker(object):
74
93
 
75
94
  @property
76
95
  def connection_string(self) -> str:
96
+ """
97
+ Constructs the SQLAlchemy connection string for the database using the provided parameters.
98
+
99
+ :return: The connection string as a string.
100
+ """
77
101
  port_part = "" if self.port is None else (":%d" % self.port)
78
102
  user_part = urllib.parse.quote(self.user, safe="")
79
103
  password_part = "" if is_blank(self.password) else (":%s" % urllib.parse.quote(self.password, safe=""))
@@ -83,15 +107,37 @@ class ConnectionMaker(object):
83
107
 
84
108
  @property
85
109
  def engine(self) -> sqlalchemy.Engine:
110
+ """
111
+ Returns a SQLAlchemy ``Engine`` instance for the configured connection string and engine options.
112
+
113
+ :return: The SQLAlchemy ``Engine``.
114
+ """
86
115
  return sqlalchemy.create_engine(self.connection_string, **self.engine_opts)
87
116
 
88
117
  def make_connection(self):
118
+ """
119
+ Establishes and returns a new database connection using the SQLAlchemy engine.
120
+
121
+ :return: A database connection object.
122
+ """
89
123
  return self.engine.connect()
90
124
 
91
125
  def make_session(self, **kwargs) -> contextlib.AbstractContextManager[sqlalchemy.orm.Session]:
126
+ """
127
+ Creates a context-managed SQLAlchemy session with the configured engine and session options.
128
+
129
+ :param kwargs: Additional keyword arguments for session creation.
130
+ :return: A context manager yielding a SQLAlchemy ``Session``.
131
+ """
92
132
  return contextlib.closing(sqlalchemy.orm.sessionmaker(self.engine, **{**self.session_opts, **kwargs})())
93
133
 
94
134
  def create_model(self, orm_base):
135
+ """
136
+ Creates all tables defined in the given ORM base using the current engine.
137
+
138
+ :param orm_base: The SQLAlchemy ORM base class.
139
+ :return: None.
140
+ """
95
141
  if packaging.version.parse(sqlalchemy.__version__) >= packaging.version.parse("2"):
96
142
  if not isinstance(orm_base, type) or not issubclass(orm_base, sqlalchemy.orm.DeclarativeBase):
97
143
  raise TypeError("not a subclass of 'sqlalchemy.orm.DeclarativeBase'")
@@ -99,6 +145,12 @@ class ConnectionMaker(object):
99
145
  orm_base.metadata.create_all(self.engine)
100
146
 
101
147
  def drop_model(self, orm_base):
148
+ """
149
+ Drops all tables defined in the given ORM base using the current engine.
150
+
151
+ :param orm_base: The SQLAlchemy ORM base class.
152
+ :return: None.
153
+ """
102
154
  if packaging.version.parse(sqlalchemy.__version__) >= packaging.version.parse("2"):
103
155
  if not isinstance(orm_base, type) or not issubclass(orm_base, sqlalchemy.orm.DeclarativeBase):
104
156
  raise TypeError("not a subclass of 'sqlalchemy.orm.DeclarativeBase'")
@@ -107,10 +159,11 @@ class ConnectionMaker(object):
107
159
 
108
160
  def execute(self, sql: str, **params):
109
161
  """
110
- Executes the given SQL statement with the specific parameters
162
+ Executes the given SQL statement with the specified parameters.
111
163
 
112
- :param sql: SQL statement to execute
113
- :param params: parameters dict
164
+ :param sql: The SQL statement to execute.
165
+ :param params: The parameters dictionary for the SQL statement.
166
+ :return: None.
114
167
  """
115
168
  with self.make_connection() as connection:
116
169
  connection.execute(sqlalchemy.text(sql), params)
@@ -118,11 +171,11 @@ class ConnectionMaker(object):
118
171
 
119
172
  def query_all(self, sql: str, **params) -> list[tuple]:
120
173
  """
121
- Executes the given SQL query with the specific parameters and returns all the results
174
+ Executes the given SQL query with the specified parameters and returns all result tuples.
122
175
 
123
- :param sql: SQL query to execute
124
- :param params: parameters dict
125
- :return: result tuples list
176
+ :param sql: The SQL query to execute.
177
+ :param params: The parameters dictionary for the SQL query.
178
+ :return: A list of result tuples.
126
179
  """
127
180
  with self.make_connection() as connection:
128
181
  with contextlib.closing(connection.execute(sqlalchemy.text(sql), params)) as proxy:
@@ -130,11 +183,12 @@ class ConnectionMaker(object):
130
183
 
131
184
  def query_first(self, sql: str, **params) -> tuple | None:
132
185
  """
133
- Executes the given SQL query with the specific parameters and returns the first result tuple
186
+ Executes the given SQL query with the specified parameters and returns the first result tuple, or ``None`` if no
187
+ results are found.
134
188
 
135
- :param sql: SQL query to execute
136
- :param params: parameters dict
137
- :return: the first result tuple
189
+ :param sql: The SQL query to execute.
190
+ :param params: The parameters dictionary for the SQL query.
191
+ :return: The first result tuple, or ``None`` if no results are found.
138
192
  """
139
193
  return head_or_none(self.query_all(sql, **params))
140
194
 
@@ -143,6 +197,13 @@ DBAdapter = ConnectionMaker
143
197
 
144
198
 
145
199
  def orm_to_dict(orm, exclude: set[str] = None) -> dict[str, Any]:
200
+ """
201
+ Converts an ORM object to a dictionary, optionally excluding specified fields.
202
+
203
+ :param orm: The ORM object to convert.
204
+ :param exclude: An optional set of field names to exclude from the result.
205
+ :return: A dictionary mapping field names to their values.
206
+ """
146
207
  if packaging.version.parse(sqlalchemy.__version__) >= packaging.version.parse("2"):
147
208
  if not isinstance(orm, sqlalchemy.orm.DeclarativeBase):
148
209
  raise TypeError("not an instance of 'sqlalchemy.orm.DeclarativeBase'")
@@ -152,6 +213,14 @@ def orm_to_dict(orm, exclude: set[str] = None) -> dict[str, Any]:
152
213
 
153
214
 
154
215
  def orm_clone(orm, exclude: set[str] = None, no_autoincrement: bool = False):
216
+ """
217
+ Creates a clone of the given ORM object, optionally excluding specified fields or auto-increment fields.
218
+
219
+ :param orm: The ORM object to clone.
220
+ :param exclude: An optional set of field names to exclude from the clone.
221
+ :param no_autoincrement: If ``True``, excludes auto-increment fields from the clone.
222
+ :return: A new ORM object with the specified fields cloned.
223
+ """
155
224
  if packaging.version.parse(sqlalchemy.__version__) >= packaging.version.parse("2"):
156
225
  if not isinstance(orm, sqlalchemy.orm.DeclarativeBase):
157
226
  raise TypeError("not an instance of 'sqlalchemy.orm.DeclarativeBase'")
@@ -173,6 +242,13 @@ def orm_clone(orm, exclude: set[str] = None, no_autoincrement: bool = False):
173
242
 
174
243
 
175
244
  def mysql_insert_ignore(enabled: bool = True):
245
+ """
246
+ Registers a SQLAlchemy compiler extension to add ``IGNORE`` to MySQL ``INSERT`` statements if ``enabled``.
247
+
248
+ :param enabled: Whether to enable the ``IGNORE`` prefix for MySQL ``INSERT`` statements.
249
+ :return: None.
250
+ """
251
+
176
252
  @sqlalchemy.ext.compiler.compiles(sqlalchemy.sql.Insert, "mysql")
177
253
  def dispatch(insert: sqlalchemy.sql.Insert, compiler: sqlalchemy.sql.compiler.SQLCompiler, **kwargs) -> str:
178
254
  if not enabled:
@@ -182,13 +258,21 @@ def mysql_insert_ignore(enabled: bool = True):
182
258
 
183
259
 
184
260
  def postgresql_insert_on_conflict_do_nothing(enabled: bool = True):
261
+ """
262
+ Registers a SQLAlchemy compiler extension to add ``ON CONFLICT DO NOTHING`` to PostgreSQL ``INSERT`` statements if
263
+ ``enabled``.
264
+
265
+ :param enabled: Whether to enable the ``ON CONFLICT DO NOTHING`` clause for PostgreSQL ``INSERT`` statements.
266
+ :return: None.
267
+ """
268
+
185
269
  @sqlalchemy.ext.compiler.compiles(sqlalchemy.sql.Insert, "postgresql")
186
270
  def dispatch(insert: sqlalchemy.sql.Insert, compiler: sqlalchemy.sql.compiler.SQLCompiler, **kwargs) -> str:
187
271
  if not enabled:
188
272
  return compiler.visit_insert(insert, **kwargs)
189
273
 
190
274
  statement = compiler.visit_insert(insert, **kwargs)
191
- # If we have a "RETURNING" clause, we must insert before it
275
+ # If we have a ``RETURNING`` clause, we must insert before it
192
276
  returning_position = statement.find("RETURNING")
193
277
  if returning_position >= 0:
194
278
  return statement[:returning_position] + " ON CONFLICT DO NOTHING " + statement[returning_position:]