datasecops-cli 0.4.4__tar.gz → 0.4.5__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 (54) hide show
  1. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/CHANGELOG.md +8 -0
  2. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/PKG-INFO +1 -1
  3. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/pyproject.toml +1 -1
  4. datasecops_cli-0.4.5/src/datasecops_cli/__init__.py +1 -0
  5. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/main.py +7 -1
  6. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/menus/downloads.py +4 -2
  7. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/bootstrap_service.py +5 -2
  8. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/dbt_project_generator.py +19 -0
  9. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/download_service.py +42 -1
  10. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/test_main.py +118 -1
  11. datasecops_cli-0.4.4/src/datasecops_cli/__init__.py +0 -1
  12. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/.github/workflows/auto-tag.yml +0 -0
  13. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/.github/workflows/publish-cli.yml +0 -0
  14. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/.gitignore +0 -0
  15. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/DEVELOPMENT.md +0 -0
  16. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/LICENSE +0 -0
  17. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/README.md +0 -0
  18. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/docs/getting-started.md +0 -0
  19. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/docs/legacy.md +0 -0
  20. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/docs/legacy_plan_of_action.md +0 -0
  21. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/docs/mcp-server.md +0 -0
  22. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/mcp-servers.json +0 -0
  23. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/setup.ps1 +0 -0
  24. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/setup.sh +0 -0
  25. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/config.py +0 -0
  26. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/menus/__init__.py +0 -0
  27. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/menus/configuration.py +0 -0
  28. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/menus/development.py +0 -0
  29. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/menus/git_operations.py +0 -0
  30. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/models/__init__.py +0 -0
  31. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/models/git_helpers.py +0 -0
  32. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/models/project_config.py +0 -0
  33. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/__init__.py +0 -0
  34. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/dbt_runner.py +0 -0
  35. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/directory_scaffolder.py +0 -0
  36. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/git_service.py +0 -0
  37. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/linting_service.py +0 -0
  38. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/skill_service.py +0 -0
  39. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/snowflake_service.py +0 -0
  40. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/upstream_service.py +0 -0
  41. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/utilities/__init__.py +0 -0
  42. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/utilities/display.py +0 -0
  43. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/utilities/file_utils.py +0 -0
  44. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/utilities/yaml_utils.py +0 -0
  45. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_mcp/__init__.py +0 -0
  46. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_mcp/__main__.py +0 -0
  47. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_mcp/connection.py +0 -0
  48. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_mcp/server.py +0 -0
  49. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/__init__.py +0 -0
  50. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/test_config.py +0 -0
  51. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/test_file_utils.py +0 -0
  52. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/test_models.py +0 -0
  53. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/test_version.py +0 -0
  54. {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/test_yaml_utils.py +0 -0
@@ -2,6 +2,14 @@
2
2
 
3
3
  All notable changes to the DataSecOps CLI are documented in this file.
4
4
 
5
+ ## [0.4.5] - 2026-05-18
6
+
7
+ ### Fixed
8
+
9
+ - **Package downloads now respect profile selection** — `datasecops download packages` and the interactive downloads menu now only include dbt packages enabled on the active profile, instead of downloading all packages from the global catalog. This matches the native app's Streamlit UI behavior.
10
+ - **Upstream package exclusion** — packages already provided by upstream projects are automatically excluded from `packages.yml`, preventing duplicate dependencies when using multi-project setups.
11
+ - **`generate_packages_yml()` filtering** — the dbt project generator now also filters packages by profile, fixing the same issue in the bootstrap/init path.
12
+
5
13
  ## [0.4.4] - 2026-05-18
6
14
 
7
15
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datasecops-cli
3
- Version: 0.4.4
3
+ Version: 0.4.5
4
4
  Summary: DataSecOps Framework CLI for Snowflake Native App
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "datasecops-cli"
7
- version = "0.4.4"
7
+ version = "0.4.5"
8
8
  description = "DataSecOps Framework CLI for Snowflake Native App"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -0,0 +1 @@
1
+ __version__ = "0.4.5"
@@ -565,7 +565,10 @@ def _run_download(config: Config, items: list[str],
565
565
  if not download_service.download_pipelines(platform=platform, profile_name=config.profile_name):
566
566
  failed = True
567
567
  elif item == "packages":
568
- if not download_service.download_dbt_packages(config.dbt_project_dir):
568
+ if not download_service.download_dbt_packages(
569
+ config.dbt_project_dir, profile=config.profile,
570
+ all_profiles=config.all_profiles
571
+ ):
569
572
  failed = True
570
573
  elif item == "macros":
571
574
  if not download_service.download_macros(config.profile_name, config.dbt_project_dir):
@@ -704,6 +707,7 @@ def _main_menu(config: Config, dbt_runner: DbtRunner, git_service: GitService,
704
707
  project_settings=config.project_settings,
705
708
  profile=config.profile,
706
709
  source_control=config.source_control,
710
+ all_profiles=config.all_profiles,
707
711
  )
708
712
  dl_menu.show()
709
713
 
@@ -722,6 +726,7 @@ def _main_menu(config: Config, dbt_runner: DbtRunner, git_service: GitService,
722
726
  project_dir=config.project_dir,
723
727
  project_settings=config.project_settings,
724
728
  profile=config.profile,
729
+ all_profiles=config.all_profiles,
725
730
  )
726
731
  bootstrap.run(platform=platform, install_skills=install_skills,
727
732
  skill_targets=skill_targets, run_deps=run_deps)
@@ -784,6 +789,7 @@ def _run_bootstrap(config: Config):
784
789
  project_dir=config.project_dir,
785
790
  project_settings=config.project_settings,
786
791
  profile=config.profile,
792
+ all_profiles=config.all_profiles,
787
793
  )
788
794
  success = bootstrap.run(platform=platform, install_skills=install_skills,
789
795
  skill_targets=skill_targets, run_deps=run_deps)
@@ -19,7 +19,7 @@ class DownloadsMenu:
19
19
  def __init__(self, download_service: DownloadService, skill_service: SkillService,
20
20
  dbt_runner: DbtRunner, profile_name: str, dbt_project_dir: Path,
21
21
  project_settings: ProjectSettings = None, profile: ProjectProfile = None,
22
- source_control: SourceControl = None):
22
+ source_control: SourceControl = None, all_profiles: list[ProjectProfile] = None):
23
23
  self.downloads = download_service
24
24
  self.skills = skill_service
25
25
  self.dbt = dbt_runner
@@ -28,6 +28,7 @@ class DownloadsMenu:
28
28
  self.project_settings = project_settings
29
29
  self.profile = profile
30
30
  self.source_control = source_control
31
+ self.all_profiles = all_profiles
31
32
 
32
33
  def show(self) -> None:
33
34
  self._menu()
@@ -46,7 +47,8 @@ class DownloadsMenu:
46
47
  complete_action()
47
48
  elif option == 3:
48
49
  display_action_header("Download dbt Packages")
49
- self.downloads.download_dbt_packages(self.dbt_project_dir)
50
+ self.downloads.download_dbt_packages(self.dbt_project_dir, profile=self.profile,
51
+ all_profiles=self.all_profiles)
50
52
  if get_input_true_false("Run dbt deps now?"):
51
53
  self.dbt.deps()
52
54
  complete_action()
@@ -14,11 +14,13 @@ class BootstrapService:
14
14
  """Bootstraps a new dbt project with all framework configuration."""
15
15
 
16
16
  def __init__(self, snowflake_service: SnowflakeService, project_dir: Path,
17
- project_settings: ProjectSettings, profile: ProjectProfile):
17
+ project_settings: ProjectSettings, profile: ProjectProfile,
18
+ all_profiles: list[ProjectProfile] = None):
18
19
  self.sf = snowflake_service
19
20
  self.project_dir = project_dir
20
21
  self.project_settings = project_settings
21
22
  self.profile = profile
23
+ self.all_profiles = all_profiles or []
22
24
  self.download_service = DownloadService(snowflake_service, project_dir)
23
25
  self.skill_service = SkillService(snowflake_service)
24
26
 
@@ -92,7 +94,8 @@ class BootstrapService:
92
94
  # Step 5: Generate packages.yml
93
95
  info_line("")
94
96
  info_line("[5] Generating packages.yml...")
95
- if self.download_service.download_dbt_packages(dbt_project_dir, profile=self.profile):
97
+ if self.download_service.download_dbt_packages(dbt_project_dir, profile=self.profile,
98
+ all_profiles=self.all_profiles):
96
99
  steps_passed += 1
97
100
  else:
98
101
  info_line(" (skipped - no dbt packages config in native app)")
@@ -165,17 +165,36 @@ def generate_profiles_yml(
165
165
  def generate_packages_yml(
166
166
  profile: ProjectProfile,
167
167
  packages_config: dict,
168
+ all_profiles: list[ProjectProfile] = None,
168
169
  ) -> str:
169
170
  """Generate packages.yml from profile's packages and upstream projects.
170
171
 
171
172
  Args:
172
173
  profile: The project profile.
173
174
  packages_config: The DBT_PACKAGES framework config.
175
+ all_profiles: All project profiles (used for upstream package exclusion).
174
176
 
175
177
  Returns:
176
178
  The YAML content as a string.
177
179
  """
178
180
  packages = packages_config.get("packages", [])
181
+
182
+ # Filter to only packages enabled on the profile
183
+ if profile.dbt_packages:
184
+ from datasecops_cli.services.download_service import _collect_upstream_packages
185
+
186
+ enabled = set(profile.dbt_packages)
187
+
188
+ # Exclude packages already provided by upstream projects
189
+ if profile.upstream_projects and all_profiles:
190
+ profile_map = {p.profile_name: p for p in all_profiles}
191
+ upstream_pkgs = _collect_upstream_packages(
192
+ profile.upstream_projects, profile_map
193
+ )
194
+ enabled -= upstream_pkgs
195
+
196
+ packages = [p for p in packages if p.get("name") in enabled]
197
+
179
198
  pkg_list: list[dict[str, Any]] = []
180
199
 
181
200
  for pkg in packages:
@@ -10,6 +10,30 @@ from datasecops_cli.utilities.display import info_line, success_line, error_line
10
10
  from datasecops_cli.utilities.file_utils import write_file, ensure_dir
11
11
 
12
12
 
13
+ def _collect_upstream_packages(
14
+ upstream_names: list[str],
15
+ profile_map: dict[str, ProjectProfile],
16
+ visited: set[str] | None = None,
17
+ ) -> set[str]:
18
+ """Recursively collect dbt package names from all upstream projects."""
19
+ if visited is None:
20
+ visited = set()
21
+ result: set[str] = set()
22
+ for name in upstream_names:
23
+ if name in visited:
24
+ continue
25
+ visited.add(name)
26
+ upstream = profile_map.get(name)
27
+ if upstream:
28
+ result.update(upstream.dbt_packages)
29
+ result.update(
30
+ _collect_upstream_packages(
31
+ upstream.upstream_projects or [], profile_map, visited
32
+ )
33
+ )
34
+ return result
35
+
36
+
13
37
  class DownloadService:
14
38
  """Downloads configurations from the native app."""
15
39
 
@@ -279,7 +303,8 @@ class DownloadService:
279
303
  success_line(f"Downloaded {count} script(s) to {scripts_dir}")
280
304
  return True
281
305
 
282
- def download_dbt_packages(self, dbt_project_dir: Path, profile: ProjectProfile = None) -> bool:
306
+ def download_dbt_packages(self, dbt_project_dir: Path, profile: ProjectProfile = None,
307
+ all_profiles: list[ProjectProfile] = None) -> bool:
283
308
  info_line("Downloading dbt package versions...")
284
309
  raw = self.sf.get_framework_config("DBT_PACKAGES")
285
310
  if not raw:
@@ -287,6 +312,22 @@ class DownloadService:
287
312
  return False
288
313
 
289
314
  packages = raw.get("packages", [])
315
+
316
+ # Filter to only packages enabled on the active profile
317
+ if profile and profile.dbt_packages:
318
+ enabled = set(profile.dbt_packages)
319
+
320
+ # Exclude packages already provided by upstream projects
321
+ if profile.upstream_projects and all_profiles:
322
+ profile_map = {p.profile_name: p for p in all_profiles}
323
+ upstream_pkgs = _collect_upstream_packages(
324
+ profile.upstream_projects, profile_map
325
+ )
326
+ enabled -= upstream_pkgs
327
+
328
+ packages = [p for p in packages if p.get("name") in enabled]
329
+ info_line(f" Filtered to {len(packages)} package(s) for profile '{profile.profile_name}'")
330
+
290
331
  pkg_list = []
291
332
  for pkg in packages:
292
333
  source = pkg.get("source", "git")
@@ -5,6 +5,7 @@ from pathlib import Path
5
5
 
6
6
  from datasecops_cli.main import _build_parser, _run_download, DOWNLOAD_ITEMS
7
7
  from datasecops_cli.config import Config
8
+ from datasecops_cli.models.project_config import ProjectProfile
8
9
 
9
10
 
10
11
  class TestBuildParser:
@@ -64,6 +65,11 @@ class TestRunDownload:
64
65
  config.project_dir = tmp_path
65
66
  config.dbt_project_dir = tmp_path
66
67
  config.profile_name = "test_profile"
68
+ config.profile = ProjectProfile(
69
+ profile_name="test_profile",
70
+ dbt_packages=["dbt_utils", "dbt_expectations"],
71
+ )
72
+ config.all_profiles = [config.profile]
67
73
  # source_control defaults to GitHub
68
74
  return config
69
75
 
@@ -119,7 +125,9 @@ class TestRunDownload:
119
125
  _run_download(config, ["packages"])
120
126
 
121
127
  assert exc.value.code == 0
122
- mock_ds.download_dbt_packages.assert_called_once_with(tmp_path)
128
+ mock_ds.download_dbt_packages.assert_called_once_with(
129
+ tmp_path, profile=config.profile, all_profiles=config.all_profiles
130
+ )
123
131
 
124
132
  @patch("datasecops_cli.main._connect_and_load")
125
133
  def test_download_macros(self, mock_connect, tmp_path):
@@ -300,3 +308,112 @@ class TestRunDownload:
300
308
  _run_download(config, ["install-dbt"])
301
309
 
302
310
  assert exc.value.code == 1
311
+
312
+
313
+ class TestDownloadPackageFiltering:
314
+ """Tests for dbt package filtering by profile."""
315
+
316
+ def test_download_dbt_packages_filters_by_profile(self, tmp_path):
317
+ """Only packages in profile.dbt_packages should appear in packages.yml."""
318
+ from datasecops_cli.services.download_service import DownloadService
319
+
320
+ mock_sf = MagicMock()
321
+ mock_sf.get_framework_config.return_value = {
322
+ "packages": [
323
+ {"name": "dbt_utils", "source": "git", "url": "https://github.com/dbt-labs/dbt-utils.git", "latest_version": "1.0.0"},
324
+ {"name": "dbt_expectations", "source": "git", "url": "https://github.com/calogica/dbt-expectations.git", "latest_version": "0.10.0"},
325
+ {"name": "dbt_constraints", "source": "git", "url": "https://github.com/Snowflake-Labs/dbt_constraints.git", "latest_version": "0.6.0"},
326
+ ]
327
+ }
328
+
329
+ profile = ProjectProfile(
330
+ profile_name="test",
331
+ dbt_packages=["dbt_utils", "dbt_expectations"],
332
+ )
333
+
334
+ service = DownloadService(mock_sf, tmp_path)
335
+ service.download_dbt_packages(tmp_path, profile=profile)
336
+
337
+ import yaml
338
+ result = yaml.safe_load((tmp_path / "packages.yml").read_text())
339
+ assert len(result["packages"]) == 2
340
+ urls = [p.get("git") for p in result["packages"]]
341
+ assert "https://github.com/dbt-labs/dbt-utils.git" in urls
342
+ assert "https://github.com/calogica/dbt-expectations.git" in urls
343
+ assert "https://github.com/Snowflake-Labs/dbt_constraints.git" not in urls
344
+
345
+ def test_download_dbt_packages_excludes_upstream(self, tmp_path):
346
+ """Packages provided by upstream projects should be excluded."""
347
+ from datasecops_cli.services.download_service import DownloadService
348
+
349
+ mock_sf = MagicMock()
350
+ mock_sf.get_framework_config.return_value = {
351
+ "packages": [
352
+ {"name": "dbt_utils", "source": "git", "url": "https://github.com/dbt-labs/dbt-utils.git", "latest_version": "1.0.0"},
353
+ {"name": "dbt_expectations", "source": "git", "url": "https://github.com/calogica/dbt-expectations.git", "latest_version": "0.10.0"},
354
+ ]
355
+ }
356
+
357
+ upstream_profile = ProjectProfile(
358
+ profile_name="upstream_project",
359
+ dbt_packages=["dbt_utils"],
360
+ )
361
+ profile = ProjectProfile(
362
+ profile_name="test",
363
+ dbt_packages=["dbt_utils", "dbt_expectations"],
364
+ upstream_projects=["upstream_project"],
365
+ )
366
+
367
+ service = DownloadService(mock_sf, tmp_path)
368
+ service.download_dbt_packages(tmp_path, profile=profile, all_profiles=[profile, upstream_profile])
369
+
370
+ import yaml
371
+ result = yaml.safe_load((tmp_path / "packages.yml").read_text())
372
+ # dbt_utils excluded (provided by upstream), only dbt_expectations + local upstream dep
373
+ git_packages = [p for p in result["packages"] if "git" in p]
374
+ local_packages = [p for p in result["packages"] if "local" in p]
375
+ assert len(git_packages) == 1
376
+ assert git_packages[0]["git"] == "https://github.com/calogica/dbt-expectations.git"
377
+ assert len(local_packages) == 1
378
+ assert local_packages[0]["local"] == "local_packages/upstream_project"
379
+
380
+ def test_download_dbt_packages_no_profile_downloads_all(self, tmp_path):
381
+ """When no profile is passed, all packages should be included."""
382
+ from datasecops_cli.services.download_service import DownloadService
383
+
384
+ mock_sf = MagicMock()
385
+ mock_sf.get_framework_config.return_value = {
386
+ "packages": [
387
+ {"name": "dbt_utils", "source": "git", "url": "https://github.com/dbt-labs/dbt-utils.git", "latest_version": "1.0.0"},
388
+ {"name": "dbt_expectations", "source": "git", "url": "https://github.com/calogica/dbt-expectations.git", "latest_version": "0.10.0"},
389
+ {"name": "dbt_constraints", "source": "git", "url": "https://github.com/Snowflake-Labs/dbt_constraints.git", "latest_version": "0.6.0"},
390
+ ]
391
+ }
392
+
393
+ service = DownloadService(mock_sf, tmp_path)
394
+ service.download_dbt_packages(tmp_path, profile=None)
395
+
396
+ import yaml
397
+ result = yaml.safe_load((tmp_path / "packages.yml").read_text())
398
+ assert len(result["packages"]) == 3
399
+
400
+ def test_download_dbt_packages_empty_dbt_packages_downloads_all(self, tmp_path):
401
+ """When profile.dbt_packages is empty, all packages should be included."""
402
+ from datasecops_cli.services.download_service import DownloadService
403
+
404
+ mock_sf = MagicMock()
405
+ mock_sf.get_framework_config.return_value = {
406
+ "packages": [
407
+ {"name": "dbt_utils", "source": "git", "url": "https://github.com/dbt-labs/dbt-utils.git", "latest_version": "1.0.0"},
408
+ {"name": "dbt_expectations", "source": "git", "url": "https://github.com/calogica/dbt-expectations.git", "latest_version": "0.10.0"},
409
+ ]
410
+ }
411
+
412
+ profile = ProjectProfile(profile_name="test", dbt_packages=[])
413
+
414
+ service = DownloadService(mock_sf, tmp_path)
415
+ service.download_dbt_packages(tmp_path, profile=profile)
416
+
417
+ import yaml
418
+ result = yaml.safe_load((tmp_path / "packages.yml").read_text())
419
+ assert len(result["packages"]) == 2
@@ -1 +0,0 @@
1
- __version__ = "0.4.4"
File without changes
File without changes
File without changes
File without changes