agglovar 0.0.1.dev2__tar.gz → 0.0.1.dev3__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 (48) hide show
  1. {agglovar-0.0.1.dev2/src/agglovar.egg-info → agglovar-0.0.1.dev3}/PKG-INFO +7 -2
  2. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/README.md +5 -0
  3. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/pyproject.toml +5 -6
  4. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar/__init__.py +7 -3
  5. agglovar-0.0.1.dev3/src/agglovar/dev/__init__.py +7 -0
  6. agglovar-0.0.1.dev3/src/agglovar/dev/imports.py +441 -0
  7. agglovar-0.0.1.dev3/src/agglovar/merge/__init__.py +19 -0
  8. agglovar-0.0.1.dev3/src/agglovar/merge/base.py +168 -0
  9. agglovar-0.0.1.dev3/src/agglovar/merge/cumulative.py +333 -0
  10. agglovar-0.0.1.dev3/src/agglovar/merge/om/_executor_base.py +98 -0
  11. agglovar-0.0.1.dev3/src/agglovar/merge/om/_executor_nr.py +90 -0
  12. agglovar-0.0.1.dev3/src/agglovar/meta/__init__.py +12 -0
  13. agglovar-0.0.1.dev3/src/agglovar/meta/decorators.py +228 -0
  14. agglovar-0.0.1.dev3/src/agglovar/meta/descriptors.py +749 -0
  15. agglovar-0.0.1.dev3/src/agglovar/pairwise/__init__.py +25 -0
  16. agglovar-0.0.1.dev3/src/agglovar/pairwise/base.py +139 -0
  17. agglovar-0.0.1.dev3/src/agglovar/pairwise/dedup.py +129 -0
  18. agglovar-0.0.1.dev2/src/agglovar/join/pair.py → agglovar-0.0.1.dev3/src/agglovar/pairwise/overlap.py +309 -281
  19. agglovar-0.0.1.dev3/src/agglovar/pairwise/sequential.py +115 -0
  20. agglovar-0.0.1.dev3/src/agglovar/pairwise/weights.py +468 -0
  21. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar/seqmatch.py +1 -1
  22. agglovar-0.0.1.dev3/src/agglovar/util/__init__.py +9 -0
  23. agglovar-0.0.1.dev3/src/agglovar/util/str.py +57 -0
  24. agglovar-0.0.1.dev3/src/agglovar/util/var.py +36 -0
  25. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3/src/agglovar.egg-info}/PKG-INFO +7 -2
  26. agglovar-0.0.1.dev3/src/agglovar.egg-info/SOURCES.txt +43 -0
  27. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar.egg-info/requires.txt +1 -1
  28. agglovar-0.0.1.dev2/src/agglovar/join/__init__.py +0 -13
  29. agglovar-0.0.1.dev2/src/agglovar/join/multipair.py +0 -145
  30. agglovar-0.0.1.dev2/src/agglovar.egg-info/SOURCES.txt +0 -27
  31. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/LICENSE +0 -0
  32. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/setup.cfg +0 -0
  33. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar/align/__init__.py +0 -0
  34. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar/align/op.py +0 -0
  35. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar/align/score.py +0 -0
  36. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar/fa.py +0 -0
  37. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar/io.py +0 -0
  38. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar/kmer/__init__.py +0 -0
  39. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar/kmer/plot.py +0 -0
  40. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar/kmer/util.py +0 -0
  41. {agglovar-0.0.1.dev2/src/agglovar/join → agglovar-0.0.1.dev3/src/agglovar/merge}/config/__init__.py +0 -0
  42. {agglovar-0.0.1.dev2/src/agglovar/join → agglovar-0.0.1.dev3/src/agglovar/merge}/config/parser.py +0 -0
  43. {agglovar-0.0.1.dev2/src/agglovar/join → agglovar-0.0.1.dev3/src/agglovar/merge}/config/stage.py +0 -0
  44. {agglovar-0.0.1.dev2/src/agglovar/join → agglovar-0.0.1.dev3/src/agglovar/merge}/config/strategy.py +0 -0
  45. {agglovar-0.0.1.dev2/src/agglovar/join → agglovar-0.0.1.dev3/src/agglovar/merge}/om/__init__.py +0 -0
  46. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar/schema.py +0 -0
  47. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar.egg-info/dependency_links.txt +0 -0
  48. {agglovar-0.0.1.dev2 → agglovar-0.0.1.dev3}/src/agglovar.egg-info/top_level.txt +0 -0
@@ -1,16 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agglovar
3
- Version: 0.0.1.dev2
3
+ Version: 0.0.1.dev3
4
4
  Summary: Toolkit for fast genomic variant transformations and intersects
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.12
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
- Requires-Dist: polars>=1.33.1
10
9
  Requires-Dist: biopython>=1.85
11
10
  Requires-Dist: edlib>=1.3.9.post1
12
11
  Requires-Dist: numpy>=2.3.0
13
12
  Requires-Dist: ply>=3.11
13
+ Requires-Dist: polars-u64-idx[numpy,pandas,pyarrow]>=1.33.1
14
14
  Provides-Extra: plot
15
15
  Requires-Dist: matplotlib>=3.10.3; extra == "plot"
16
16
  Provides-Extra: all
@@ -32,9 +32,14 @@ The name Agglovar is a portmanteau of the latin word "agglomerare" (to gather) a
32
32
 
33
33
  Agglovar is under active development and a stable release is not yet available.
34
34
 
35
+ ## Documentation
36
+
37
+ Documentation for Agglovar can be found at:
38
+ https://agglovar.readthedocs.io/en/latest
35
39
 
36
40
  ## Installation
37
41
 
38
42
  ```
39
43
  pip install agglovar
40
44
  ```
45
+
@@ -13,9 +13,14 @@ The name Agglovar is a portmanteau of the latin word "agglomerare" (to gather) a
13
13
 
14
14
  Agglovar is under active development and a stable release is not yet available.
15
15
 
16
+ ## Documentation
17
+
18
+ Documentation for Agglovar can be found at:
19
+ https://agglovar.readthedocs.io/en/latest
16
20
 
17
21
  ## Installation
18
22
 
19
23
  ```
20
24
  pip install agglovar
21
25
  ```
26
+
@@ -11,16 +11,16 @@ readme = "README.md"
11
11
 
12
12
  requires-python = ">=3.12"
13
13
  dependencies = [
14
- "polars>=1.33.1",
15
14
  "biopython>=1.85",
16
15
  "edlib>=1.3.9.post1",
17
16
  "numpy>=2.3.0",
18
17
  "ply>=3.11",
18
+ "polars-u64-idx[numpy,pandas,pyarrow]>=1.33.1",
19
19
  ]
20
20
 
21
21
  [agglovar.urls]
22
22
  Homepage = "https://github.com/BeckLaboratory/agglovar"
23
- #Documentation = "https://agglovar.readthedocs.io"
23
+ Documentation = "https://agglovar.readthedocs.io/en/latest"
24
24
  Repository = "https://github.com/BeckLaboratory/agglovar"
25
25
  Issues = "https://github.com/BeckLaboratory/agglovar/issues"
26
26
  Changelog = "https://github.com/BeckLaboratory/agglovar/blob/master/CHANGELOG.md"
@@ -39,7 +39,6 @@ classifiers = [
39
39
  "Operating System :: OS Independent",
40
40
  "Programming Language :: Python :: 3",
41
41
  "Programming Language :: Python :: 3.12",
42
- "Private :: Do Not Upload", # Temporary until published
43
42
  ]
44
43
 
45
44
  authors = [
@@ -85,7 +84,7 @@ build-backend = "setuptools.build_meta"
85
84
 
86
85
  [tool.setuptools.packages.find]
87
86
  where = ["src"]
88
- include = ["agglovar"]
87
+ include = ["agglovar", "agglovar.*"]
89
88
  exclude = ["*__pycache__"]
90
89
 
91
90
  [tool.setuptools.dynamic]
@@ -103,8 +102,8 @@ max-line-length = 120
103
102
 
104
103
  [tool.flake8]
105
104
  max-line-length = 120
106
- docstring-convention = "google"
105
+ docstring-convention = "pep257"
107
106
  per-file-ignores = [
108
107
  "src/agglovar/join/config/parser.py: D200, D205, D212, D300, D403, D415"
109
108
  ]
110
- rst-roles = ["class", "meth", "const", "ref", "func"]
109
+ rst-roles = ["class", "meth", "const", "ref", "func", "mod"]
@@ -1,21 +1,25 @@
1
1
  """Agglovar: A toolkit for fast genomic variant transformations and intersects."""
2
2
 
3
- __version__ = '0.0.1.dev2'
3
+ __version__ = '0.0.1.dev3'
4
4
 
5
5
  __all__ = [
6
6
  'align',
7
7
  'fa',
8
- 'join',
8
+ 'pairwise',
9
9
  'io',
10
10
  'kmer',
11
+ 'merge',
12
+ 'meta',
11
13
  'schema',
12
14
  'seqmatch',
13
15
  ]
14
16
 
15
17
  from . import align
16
18
  from . import fa
17
- from . import join
19
+ from . import pairwise
18
20
  from . import io
19
21
  from . import kmer
22
+ from . import merge
23
+ from . import meta
20
24
  from . import schema
21
25
  from . import seqmatch
@@ -0,0 +1,7 @@
1
+ """Developer utilities, not used by the main code."""
2
+
3
+ __all__ = [
4
+ 'imports'
5
+ ]
6
+
7
+ from . import imports
@@ -0,0 +1,441 @@
1
+ """General development utilities."""
2
+
3
+ __all__ = [
4
+ 'GlobalImport',
5
+ 'alias_to_obj_dict',
6
+ 'find_imports',
7
+ 'get_defined_names',
8
+ 'get_module_definitions',
9
+ ]
10
+
11
+ import ast
12
+ from dataclasses import dataclass, field
13
+ import importlib
14
+ import inspect
15
+ import re
16
+ from types import ModuleType
17
+ from typing import Any, Generator, Optional
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class _GlobalImportScope:
22
+ """Tracks one item in a stack of global imports.
23
+
24
+ :ivar package_path: Name of the module where import statements are relative to (e.g. 'pav3.lgsv.interval' for
25
+ imports in 'pav3/lgsv/interval.py').
26
+ :ivar import_statements: A string of newline-separated import statements processed.
27
+ :ivar global_dict: A dictionary mapping names of objects added to the global scope to the objects added. Used when
28
+ removing a scope.
29
+ """
30
+
31
+ package_path: str
32
+ import_statements: Optional[str] = field(repr=False)
33
+ overwrite: bool
34
+ caller_globals: dict[str, Any]
35
+ mod_imports: bool = True
36
+ mod_private: bool = True
37
+ mod_dunder: bool = False
38
+ global_dict: dict = field(default_factory=dict, init=False, repr=False)
39
+ restore_dict: dict = field(default_factory=dict, init=False, repr=False)
40
+
41
+ def __post_init__(self) -> None:
42
+ """Populate global_dict."""
43
+ for alias, obj in alias_to_obj_dict(self.package_path, self.import_statements).items():
44
+ self.global_dict[alias] = obj
45
+
46
+ def apply(self) -> None:
47
+ """Apply this scope to the global scope."""
48
+ if not self.overwrite:
49
+ for alias in set(self.global_dict.keys()) & (set(self.caller_globals.keys())):
50
+ if self.caller_globals[alias] != self.global_dict[alias]:
51
+ raise ValueError(f'Alias "{alias}" is defined in globals')
52
+
53
+ for alias in self.global_dict.keys():
54
+ if alias in self.caller_globals:
55
+ self.restore_dict[alias] = self.caller_globals[alias]
56
+ self.caller_globals[alias] = self.global_dict[alias]
57
+
58
+ def restore(self) -> None:
59
+ """Restore the global scope to the state before this scope was applied."""
60
+ for alias in self.global_dict.keys():
61
+ if alias not in self.caller_globals or self.caller_globals[alias] != self.global_dict[alias]:
62
+ continue
63
+
64
+ if alias in self.restore_dict:
65
+ self.caller_globals[alias] = self.restore_dict[alias]
66
+ else:
67
+ del self.caller_globals[alias]
68
+
69
+
70
+ class GlobalImport:
71
+ """Mantains a stack of import frames."""
72
+
73
+ def __init__(self) -> None:
74
+ """Initialize impot stack."""
75
+ self.import_stack = []
76
+ self.caller_globals = inspect.currentframe().f_back.f_globals
77
+
78
+ def push(
79
+ self,
80
+ package_path: str,
81
+ import_statements: Optional[str] = None,
82
+ overwrite: bool = True,
83
+ mod_imports: bool = True,
84
+ mod_private: bool = True,
85
+ mod_dunder: bool = False,
86
+ ) -> None:
87
+ """Push a new scope to the global import stack and update the global scope.
88
+
89
+ :param package_path: Name of the module where this import statement is found (e.g. 'pav3.lgsv.interval' if
90
+ the import statements are in 'pav3/lgsv/interval.py').
91
+ :param import_statements: A string of newline-separated import statements.
92
+ :param overwrite: Whether to overwrite existing global variables.
93
+ :param mod_imports: Whether to import objects from modules.
94
+ :param mod_private: Whether to import private objects from modules.
95
+ :param mod_dunder: Whether to import dunder objects from modules.
96
+ """
97
+ scope = _GlobalImportScope(
98
+ package_path=package_path,
99
+ import_statements=import_statements,
100
+ overwrite=overwrite,
101
+ caller_globals=self.caller_globals,
102
+ mod_imports=mod_imports,
103
+ mod_private=mod_private,
104
+ mod_dunder=mod_dunder,
105
+ )
106
+
107
+ scope.apply()
108
+
109
+ self.import_stack.append(scope)
110
+
111
+ def pop(self) -> bool:
112
+ """
113
+ Pop the last scope from the stack and restore the global scope to the state before it was applied.
114
+
115
+ :returns: True if a scope was popped, False if the global import stack is empty.
116
+ """
117
+ if not self.import_stack:
118
+ return False
119
+
120
+ self.import_stack.pop().restore()
121
+
122
+ return True
123
+
124
+ def refresh(self) -> None:
125
+ """Undo and re-do all scopes.
126
+
127
+ Run when objects might have changed.
128
+ """
129
+ old_state_stack = []
130
+
131
+ while self.import_stack:
132
+ old_state = self.import_stack.pop()
133
+ old_state_stack.append(old_state)
134
+ old_state.restore()
135
+
136
+ while old_state_stack:
137
+ old_state = old_state_stack.pop()
138
+ self.push(old_state.package_path, old_state.import_statements, old_state.overwrite)
139
+
140
+ def clear(self) -> None:
141
+ """Clear all scopes."""
142
+ while self.pop():
143
+ pass
144
+
145
+ def __len__(self) -> int:
146
+ """Get the number of stack items."""
147
+ return len(self.import_stack)
148
+
149
+ def __bool__(self) -> bool:
150
+ """Determine if the stack is non-empty."""
151
+ return bool(self.import_stack)
152
+
153
+
154
+ def alias_to_obj_dict(
155
+ package_path: str,
156
+ import_statements: Optional[str] = None,
157
+ mod_imports: bool = True,
158
+ mod_private: bool = True,
159
+ mod_dunder: bool = False,
160
+ ) -> dict[str, Any]:
161
+ """Find objects for relative imports.
162
+
163
+ :param package_path: Name of the module where this import statement is found (i.e. 'pav3.lgsv.interval' if the).
164
+ :param import_statements: A string of newline-separated import statements.
165
+ :param mod_imports: Whether to import objects from modules.
166
+ :param mod_private: Whether to import private objects from modules.
167
+ :param mod_dunder: Whether to import dunder objects from modules.
168
+
169
+ :returns: A set of new global names that were imported.
170
+ """
171
+ import_path = package_path.split('.')
172
+
173
+ from_match_pattern = re.compile(r'^from\s+(\.*)([a-zA-Z_][a-zA-Z0-9_.]*)?\s+import\s+(.+)\s*$')
174
+ as_pattern = re.compile(r'\s+as\s+')
175
+
176
+ import_items = [] # List of tuples: (absolute import path, alias, original line)
177
+
178
+ # Import statements
179
+ if import_statements:
180
+ for line in import_statements.split('\n'):
181
+ line = line.strip()
182
+
183
+ if not line or line.startswith('#'):
184
+ continue
185
+
186
+ # Handle "from ... import ..." statements
187
+ if from_match := from_match_pattern.match(line):
188
+ dots, module_path, imports = from_match.groups()
189
+ dot_count = len(dots)
190
+
191
+ # Calculate absolute module path
192
+ if dot_count == 0:
193
+ # Absolute import
194
+ abs_module = module_path
195
+
196
+ else:
197
+ # Relative import
198
+ if dot_count > len(import_path):
199
+ raise ValueError(f"Attempted relative import beyond top-level package: {line}")
200
+
201
+ abs_module = (
202
+ (
203
+ '.'.join(
204
+ import_path[:-(dot_count)] if dot_count > 0 else import_path
205
+ )
206
+ ) + (
207
+ ('.' + module_path)
208
+ if module_path is not None else ''
209
+ )
210
+ )
211
+
212
+ # Translate all imports
213
+ import_set = {item.strip() for item in imports.split(',') if item.strip()}
214
+
215
+ # Parse imports (handle multiple imports and aliases)
216
+ #
217
+ # * Module name
218
+ # * Object name
219
+ # * Alias
220
+ # * Original line (string)
221
+ import_items.extend([
222
+ (
223
+ abs_module,
224
+ items[0],
225
+ items[1] if len(items) > 1 else items[0],
226
+ line
227
+ )
228
+ for items in [
229
+ as_pattern.split(item.strip()) for item in import_set
230
+ ]
231
+ ])
232
+
233
+ else:
234
+ raise ValueError(f'Unrecognized import structure: {line}')
235
+
236
+ # Collect defined names
237
+ if mod_imports:
238
+ mod = importlib.import_module(package_path)
239
+
240
+ for name, def_type, mod_id, mod_name in get_defined_names(mod):
241
+
242
+ if name is None:
243
+ continue # Import statements, ignore (not from ... import)
244
+
245
+ # Dunder
246
+ if not mod_dunder and (name.startswith('__') and name.endswith('__')):
247
+ continue
248
+
249
+ # Private
250
+ if not mod_private and name.startswith('_'):
251
+ continue
252
+
253
+ # Do import
254
+ if def_type == 'import_from':
255
+ if not mod_imports:
256
+ continue
257
+
258
+ import_items.append((mod_id, mod_name, name, f'<from {mod_id} import {mod_name} as {name}>'))
259
+
260
+ elif def_type in {'func', 'class', 'def'}:
261
+ import_items.append((package_path, name, name, f'<{def_type} {name}>'))
262
+
263
+ # Run imports
264
+ global_dict = dict()
265
+
266
+ for module_name, object_name, alias, line in import_items:
267
+
268
+ try:
269
+ obj = getattr(importlib.import_module(module_name, package_path), object_name)
270
+
271
+ except ImportError as e:
272
+ raise ImportError(f"Failed to import '{module_name}': line=\"{line}\"") from e
273
+
274
+ except NameError as e:
275
+ raise NameError(
276
+ f"Failed to import '{module_name}.{object_name}': Object name not found in module: line=\"{line}\""
277
+ ) from e
278
+
279
+ if alias in global_dict and global_dict[alias] != obj:
280
+ raise ValueError(
281
+ f"Alias '{alias}' is used multiple times in stmts with conflicting objects: \"{line}\": "
282
+ f"existing=\"{global_dict[alias]}\" (0x{id(global_dict[alias]):X}), new=\"{obj}\" (0x{id(obj):X})"
283
+ )
284
+
285
+ global_dict[alias] = obj
286
+
287
+ # Return new global names
288
+ return global_dict
289
+
290
+
291
+ def find_imports(
292
+ module_name: str,
293
+ public_only: bool = True,
294
+ all_only: bool = False
295
+ ) -> set[str]:
296
+ """Locate objects defined in a module.
297
+
298
+ :param module_name: Name of the module to inspect.
299
+ :param public_only: Whether to include only public objects (i.e. not starting with '_').
300
+ :param all_only: Whether to include only objects defined in '__all__'. Overrides public_only if __all__ has values.
301
+
302
+ :returns: Set of object names defined in the module.
303
+ """
304
+ module = importlib.import_module(module_name)
305
+
306
+ all_module_names = set(get_defined_names(module))
307
+
308
+ defined_in_all = set(module.__all__) if hasattr(module, '__all__') else set()
309
+
310
+ if missing_defs := defined_in_all - all_module_names:
311
+ raise ValueError(
312
+ f'Module "{module_name}" defines "__all__" with missing definitions: '
313
+ f'{", ".join(sorted(missing_defs))}'
314
+ )
315
+
316
+ if hidden_defs := {name for name in defined_in_all if name.startswith('_')}:
317
+ raise ValueError(
318
+ f'Module "{module_name}" defines "__all__" with "hidden" definitions (starting with "_"): '
319
+ f'{", ".join(sorted(hidden_defs))}'
320
+ )
321
+
322
+ if all_only:
323
+ if not defined_in_all:
324
+ raise ValueError(f'"all_only" was set, but module "{module_name}" does not define "__all__"')
325
+
326
+ module_names = defined_in_all
327
+
328
+ else:
329
+ module_names = all_module_names
330
+
331
+ if public_only:
332
+ module_names = {name for name in module_names if not name.startswith('_')}
333
+
334
+ return module_names
335
+
336
+
337
+ def get_defined_names(
338
+ mod: ModuleType
339
+ ):
340
+ """
341
+ Get names defined in a module.
342
+
343
+ :param mod: Module to inspect.
344
+
345
+ :yields: Tuples of (name, type, level) where "type" is "func", "class", "def", or "import". "level" is defined for
346
+ imports (None for others) and is the level of the import with 0 being the top-level and 1 or more being a
347
+ relative import.
348
+ """
349
+
350
+ tree = ast.parse(inspect.getsource(mod))
351
+
352
+ for node in tree.body:
353
+
354
+ if isinstance(node, ast.FunctionDef):
355
+ yield node.name, 'func', None, None
356
+
357
+ elif isinstance(node, ast.ClassDef):
358
+ yield node.name, 'class', None, None
359
+
360
+ elif isinstance(node, ast.Assign):
361
+ for target in node.targets:
362
+ yield target.id, 'def', None, None
363
+
364
+ elif isinstance(node, ast.AnnAssign):
365
+ yield node.target.id, 'def', None, None
366
+
367
+ elif isinstance(node, ast.ImportFrom):
368
+
369
+ module_name = (
370
+ '.'.join(mod.__name__.split('.')[:-node.level]
371
+ + (node.module.split('.') if node.module else []))
372
+ )
373
+
374
+ for node_name in node.names:
375
+ yield (
376
+ node_name.asname if node_name.asname else node_name.name,
377
+ 'import_from',
378
+ module_name,
379
+ node_name.name,
380
+ )
381
+
382
+ elif isinstance(node, ast.Import):
383
+ for node_name in node.names:
384
+ yield None, 'import', node_name.name, node_name.name
385
+
386
+ elif isinstance(node, ast.Expr):
387
+ pass
388
+
389
+ else:
390
+ raise ValueError(f'Unknown type: {type(node)}')
391
+
392
+
393
+ # def get_defined_names(module: ModuleType) -> list[str]:
394
+ # """Get a list of objects defined by the module ignoring dunder objects and imported modules."""
395
+ # name_list = []
396
+ #
397
+ # all_names = set(module.__all__) if hasattr(module, '__all__') else set()
398
+ #
399
+ # for obj_name, obj in inspect.getmembers(module):
400
+ #
401
+ # if obj_name in all_names:
402
+ # name_list.append(obj_name)
403
+ # continue
404
+ #
405
+ # if obj_name.startswith('__'):
406
+ # continue
407
+ #
408
+ # if hasattr(obj, '__module__'):
409
+ # if obj.__module__ == module.__name__:
410
+ # name_list.append(obj_name)
411
+ # else:
412
+ # if not isinstance(obj, ModuleType):
413
+ # name_list.append(obj_name)
414
+ #
415
+ # return name_list
416
+
417
+
418
+ def get_module_definitions(
419
+ module: ModuleType,
420
+ include_dunder: bool = False
421
+ ) -> Generator[str, None, None]:
422
+ """Get names defined in a module object ignoring imports.
423
+
424
+ :param module: Module object to inspect.
425
+ :param include_dunder: Whether to include dunder objects (starting with '__').
426
+ """
427
+ source = inspect.getsource(module)
428
+ tree = ast.parse(source)
429
+
430
+ for node in tree.body:
431
+
432
+ if isinstance(node, (ast.FunctionDef, ast.ClassDef)):
433
+ if include_dunder or not node.name.startswith('__'):
434
+ yield node.name
435
+
436
+ elif isinstance(node, ast.Assign):
437
+ for target in node.targets:
438
+ if isinstance(target, ast.Name):
439
+ if include_dunder or not target.id.startswith('__'):
440
+ yield target.id
441
+
@@ -0,0 +1,19 @@
1
+ """Create one merged callset derived from one or more variant tables.
2
+
3
+ Merging variants is the process of combining variants from one or more tables into one merged
4
+ callset. The merged table is a variant table containing key variant columns, such as genomic
5
+ location and an ID. Merged tables may include other columns derived from the input tables, such as
6
+ lists of fields or statistics derived from them.
7
+ """
8
+
9
+ __all__ = [
10
+ # 'config',
11
+ # 'om',
12
+ 'base',
13
+ 'cumulative',
14
+ ]
15
+
16
+ # from . import config
17
+ # from . import om
18
+ from . import base
19
+ from . import cumulative