half-orm-dev 0.17.0a10__tar.gz → 0.17.0a11__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 (68) hide show
  1. {half_orm_dev-0.17.0a10/half_orm_dev.egg-info → half_orm_dev-0.17.0a11}/PKG-INFO +6 -17
  2. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/patch_manager.py +93 -3
  3. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/repo.py +9 -12
  4. half_orm_dev-0.17.0a11/half_orm_dev/templates/pyproject.toml +55 -0
  5. half_orm_dev-0.17.0a11/half_orm_dev/version.txt +1 -0
  6. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11/half_orm_dev.egg-info}/PKG-INFO +6 -17
  7. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev.egg-info/SOURCES.txt +3 -4
  8. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev.egg-info/top_level.txt +0 -1
  9. half_orm_dev-0.17.0a11/pyproject.toml +54 -0
  10. half_orm_dev-0.17.0a11/setup.py +49 -0
  11. half_orm_dev-0.17.0a10/half_orm_dev/templates/setup.py +0 -81
  12. half_orm_dev-0.17.0a10/half_orm_dev/version.txt +0 -1
  13. half_orm_dev-0.17.0a10/setup.py +0 -80
  14. half_orm_dev-0.17.0a10/tests/__init__.py +0 -0
  15. half_orm_dev-0.17.0a10/tests/conftest.py +0 -335
  16. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/AUTHORS +0 -0
  17. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/LICENSE +0 -0
  18. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/README.md +0 -0
  19. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/__init__.py +0 -0
  20. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/__init__.py +0 -0
  21. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/__init__.py +0 -0
  22. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/apply.py +0 -0
  23. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/check.py +0 -0
  24. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/clone.py +0 -0
  25. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/init.py +0 -0
  26. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/new.py +0 -0
  27. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/patch.py +0 -0
  28. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/release.py +0 -0
  29. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/restore.py +0 -0
  30. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/sync.py +0 -0
  31. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/todo.py +0 -0
  32. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/undo.py +0 -0
  33. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/update.py +0 -0
  34. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/commands/upgrade.py +0 -0
  35. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli/main.py +0 -0
  36. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/cli_extension.py +0 -0
  37. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/database.py +0 -0
  38. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/decorators.py +0 -0
  39. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/hgit.py +0 -0
  40. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/hop.py +0 -0
  41. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/manifest.py +0 -0
  42. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/modules.py +0 -0
  43. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/patch.py +0 -0
  44. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/patch_validator.py +0 -0
  45. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +0 -0
  46. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +0 -0
  47. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +0 -0
  48. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/patches/log +0 -0
  49. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/patches/sql/half_orm_meta.sql +0 -0
  50. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/release_manager.py +0 -0
  51. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/.gitignore +0 -0
  52. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/MANIFEST.in +0 -0
  53. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/Pipfile +0 -0
  54. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/README +0 -0
  55. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/conftest_template +0 -0
  56. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/git-hooks/pre-commit +0 -0
  57. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/git-hooks/prepare-commit-msg +0 -0
  58. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/init_module_template +0 -0
  59. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/module_template_1 +0 -0
  60. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/module_template_2 +0 -0
  61. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/module_template_3 +0 -0
  62. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/relation_test +0 -0
  63. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/sql_adapter +0 -0
  64. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/templates/warning +0 -0
  65. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev/utils.py +0 -0
  66. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev.egg-info/dependency_links.txt +0 -0
  67. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/half_orm_dev.egg-info/requires.txt +1 -1
  68. {half_orm_dev-0.17.0a10 → half_orm_dev-0.17.0a11}/setup.cfg +0 -0
@@ -1,42 +1,31 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 0.17.0a10
3
+ Version: 0.17.0a11
4
4
  Summary: half_orm development Framework.
5
- Home-page: https://github.com/collorg/halfORM_dev
6
- Author: Joël Maïzi
7
- Author-email: joel.maizi@collorg.org
8
- License: GNU General Public License v3 (GPLv3)
5
+ Author-email: Joël Maïzi <joel.maizi@collorg.org>
6
+ License-Expression: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/collorg/halfORM_dev
9
8
  Keywords: postgres,relation-object mapping
10
9
  Classifier: Development Status :: 3 - Alpha
11
10
  Classifier: Intended Audience :: Developers
12
11
  Classifier: Topic :: Software Development :: Build Tools
13
- Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
14
- Classifier: Programming Language :: Python :: 3.8
15
12
  Classifier: Programming Language :: Python :: 3.9
16
13
  Classifier: Programming Language :: Python :: 3.10
17
14
  Classifier: Programming Language :: Python :: 3.11
18
15
  Classifier: Programming Language :: Python :: 3.12
19
16
  Classifier: Programming Language :: Python :: 3.13
20
17
  Classifier: Programming Language :: Python :: 3.14
18
+ Requires-Python: >=3.9
21
19
  Description-Content-Type: text/markdown
22
20
  License-File: LICENSE
23
21
  License-File: AUTHORS
24
22
  Requires-Dist: GitPython
25
23
  Requires-Dist: click
26
24
  Requires-Dist: pydash
27
- Requires-Dist: half_orm<0.18.0,>=0.17.0
28
25
  Requires-Dist: pytest
29
- Dynamic: author
30
- Dynamic: author-email
31
- Dynamic: classifier
32
- Dynamic: description
33
- Dynamic: description-content-type
34
- Dynamic: home-page
35
- Dynamic: keywords
36
- Dynamic: license
26
+ Requires-Dist: half_orm<0.18.0,>=0.17.0
37
27
  Dynamic: license-file
38
28
  Dynamic: requires-dist
39
- Dynamic: summary
40
29
 
41
30
  # half_orm_dev
42
31
 
@@ -15,6 +15,7 @@ import time
15
15
  from pathlib import Path
16
16
  from typing import List, Dict, Optional, Tuple, Any
17
17
  from dataclasses import dataclass
18
+ import click
18
19
  from git.exc import GitCommandError
19
20
 
20
21
  from half_orm import utils
@@ -1588,7 +1589,7 @@ class PatchManager:
1588
1589
  2. Create temporary validation branch from release branch
1589
1590
  3. Merge patch into temp branch
1590
1591
  4. Run patch apply and verify no modifications
1591
- 5. Run tests (TODO: implement test runner)
1592
+ 5. Run tests (best-effort if available)
1592
1593
  6. Cleanup temp branch
1593
1594
  7. Return to original branch
1594
1595
 
@@ -1680,8 +1681,8 @@ class PatchManager:
1680
1681
  f"Failed to run patch apply during validation: {e}"
1681
1682
  )
1682
1683
 
1683
- # 4. Run tests (TODO: implement test runner)
1684
- click.echo(f" • {utils.Color.blue('⚠')} Skipping tests (not implemented yet)")
1684
+ # 4. Run tests (best-effort)
1685
+ self._run_tests_if_available()
1685
1686
 
1686
1687
  click.echo(f" • {utils.Color.green('✓')} Validation passed!\n")
1687
1688
 
@@ -1699,6 +1700,95 @@ class PatchManager:
1699
1700
  # Cleanup errors are non-critical, just warn
1700
1701
  click.echo(f"⚠️ Warning: Failed to cleanup temp branch {temp_branch}: {e}")
1701
1702
 
1703
+ def _run_tests_if_available(self) -> None:
1704
+ """
1705
+ Run tests if test configuration is available.
1706
+
1707
+ Detects if the project has test configuration (pytest.ini, pyproject.toml
1708
+ with pytest config, etc.) and runs pytest if available.
1709
+ - If no test config found: skip silently
1710
+ - If tests fail: raise PatchManagerError (BLOCKS workflow)
1711
+ - If tests pass: success message
1712
+ - If pytest not installed: warning but continue
1713
+
1714
+ This ensures code quality by blocking patches with failing tests.
1715
+
1716
+ Raises:
1717
+ PatchManagerError: If tests fail
1718
+
1719
+ Examples:
1720
+ self._run_tests_if_available()
1721
+ # With tests configured and passing → ✓ Tests passed
1722
+ # With tests configured but failing → raises PatchManagerError
1723
+ # Without test config → skips silently
1724
+ """
1725
+ base_dir = Path(self._repo.base_dir)
1726
+
1727
+ # Check if test configuration exists
1728
+ test_config_files = [
1729
+ base_dir / "pytest.ini",
1730
+ base_dir / "pyproject.toml",
1731
+ base_dir / "setup.cfg",
1732
+ base_dir / "tox.ini"
1733
+ ]
1734
+
1735
+ has_test_config = any(f.exists() for f in test_config_files)
1736
+
1737
+ # Also check if tests directory exists
1738
+ tests_dir = base_dir / "tests"
1739
+ has_tests_dir = tests_dir.exists() and tests_dir.is_dir()
1740
+
1741
+ if not has_test_config and not has_tests_dir:
1742
+ # No test config - skip silently (project may not have tests yet)
1743
+ return
1744
+
1745
+ # Try to run pytest
1746
+ try:
1747
+ click.echo(f" • Running tests...")
1748
+ result = subprocess.run(
1749
+ ["pytest", "-v", "--tb=short"],
1750
+ cwd=str(base_dir),
1751
+ capture_output=True,
1752
+ text=True
1753
+ # No timeout - user can Ctrl+C if needed
1754
+ # Cleanup (temp branch + lock) is protected by finally blocks
1755
+ )
1756
+
1757
+ if result.returncode == 0:
1758
+ click.echo(f" • {utils.Color.green('✓')} Tests passed")
1759
+ else:
1760
+ # Tests failed - BLOCK the workflow
1761
+ error_msg = f"Tests failed! Cannot close patch with failing tests.\n\n"
1762
+
1763
+ if result.stdout:
1764
+ # Show test output
1765
+ error_msg += "Test output:\n"
1766
+ output_lines = result.stdout.strip().split('\n')
1767
+ # Show last 20 lines to give enough context
1768
+ last_lines = output_lines[-20:] if len(output_lines) > 20 else output_lines
1769
+ for line in last_lines:
1770
+ error_msg += f" {line}\n"
1771
+
1772
+ if result.stderr:
1773
+ error_msg += f"\nErrors:\n{result.stderr}\n"
1774
+
1775
+ error_msg += "\nFix the failing tests before closing the patch."
1776
+ raise PatchManagerError(error_msg)
1777
+
1778
+ except FileNotFoundError:
1779
+ # pytest not installed - warn but don't block
1780
+ click.echo(f" • {utils.Color.yellow('⚠')} pytest not found (install pytest to run tests)")
1781
+ except PatchManagerError:
1782
+ # Re-raise our own exceptions (test failures)
1783
+ raise
1784
+ except Exception as e:
1785
+ # Any other error - warn but don't block (might be environment issue)
1786
+ click.echo(f" • {utils.Color.yellow('⚠')} Failed to run tests: {e} (continuing anyway)")
1787
+
1788
+ # Note: KeyboardInterrupt (Ctrl+C) is not caught here - it inherits from
1789
+ # BaseException, not Exception, so it will propagate up to the decorator
1790
+ # where the lock will be properly released in the finally block
1791
+
1702
1792
  def _validate_on_ho_release(self) -> str:
1703
1793
  """
1704
1794
  Validate that current branch is ho-release/X.Y.Z.
@@ -291,15 +291,15 @@ class Repo:
291
291
  else:
292
292
  utils.error(f"ERROR! The path '{self.__base_dir}' already exists!\n", exit_code=1)
293
293
  readme = utils.read(os.path.join(TEMPLATE_DIRS, 'README'))
294
- setup_template = utils.read(os.path.join(TEMPLATE_DIRS, 'setup.py'))
294
+ pyproject_template = utils.read(os.path.join(TEMPLATE_DIRS, 'pyproject.toml'))
295
295
  git_ignore = utils.read(os.path.join(TEMPLATE_DIRS, '.gitignore'))
296
296
  pipfile = utils.read(os.path.join(TEMPLATE_DIRS, 'Pipfile'))
297
297
 
298
- setup = setup_template.format(
298
+ pyproject = pyproject_template.format(
299
299
  dbname=self.__config.name,
300
300
  package_name=self.__config.name,
301
301
  half_orm_version=half_orm.__version__)
302
- utils.write(os.path.join(self.__base_dir, 'setup.py'), setup)
302
+ utils.write(os.path.join(self.__base_dir, 'pyproject.toml'), pyproject)
303
303
 
304
304
  pipfile = pipfile.format(
305
305
  half_orm_version=half_orm.__version__,
@@ -527,7 +527,7 @@ class Repo:
527
527
  7. Initialize Database instance (self.database)
528
528
  8. Generate Python package structure
529
529
  9. Initialize Git repository with ho-prod branch
530
- 10. Generate template files (README, .gitignore, setup.py, Pipfile)
530
+ 10. Generate template files (README, .gitignore, pyproject.toml, Pipfile)
531
531
  11. Save model/schema-0.0.0.sql
532
532
 
533
533
  Git-centric Architecture:
@@ -1303,31 +1303,28 @@ See docs/half_orm_dev.md for complete documentation.
1303
1303
  Creates standard project files:
1304
1304
  - README.md: Project documentation
1305
1305
  - .gitignore: Git exclusions
1306
- - setup.py: Python packaging (current template)
1306
+ - pyproject.toml: Python packaging (PEP 517/518)
1307
1307
  - Pipfile: Dependencies (current template)
1308
1308
 
1309
1309
  Templates read from TEMPLATE_DIRS and formatted with project variables.
1310
1310
 
1311
- Note: Future enhancement will migrate to pyproject.toml,
1312
- but keeping current templates for initial implementation.
1313
-
1314
1311
  Examples:
1315
1312
  _generate_template_files()
1316
- # Creates: README.md, .gitignore, setup.py, Pipfile
1313
+ # Creates: README.md, .gitignore, pyproject.toml, Pipfile
1317
1314
  """
1318
1315
  import half_orm
1319
1316
  from half_orm_dev.utils import TEMPLATE_DIRS, hop_version
1320
1317
 
1321
1318
  # Read templates
1322
1319
  readme_template = utils.read(os.path.join(TEMPLATE_DIRS, 'README'))
1323
- setup_template = utils.read(os.path.join(TEMPLATE_DIRS, 'setup.py'))
1320
+ pyproject_template = utils.read(os.path.join(TEMPLATE_DIRS, 'pyproject.toml'))
1324
1321
  git_ignore = utils.read(os.path.join(TEMPLATE_DIRS, '.gitignore'))
1325
1322
  pipfile_template = utils.read(os.path.join(TEMPLATE_DIRS, 'Pipfile'))
1326
1323
 
1327
1324
  # Format templates with project variables
1328
1325
  package_name = self.__config.name
1329
1326
 
1330
- setup = setup_template.format(
1327
+ pyproject = pyproject_template.format(
1331
1328
  dbname=package_name,
1332
1329
  package_name=package_name,
1333
1330
  half_orm_version=half_orm.__version__
@@ -1345,7 +1342,7 @@ See docs/half_orm_dev.md for complete documentation.
1345
1342
  )
1346
1343
 
1347
1344
  # Write files
1348
- utils.write(os.path.join(self.__base_dir, 'setup.py'), setup)
1345
+ utils.write(os.path.join(self.__base_dir, 'pyproject.toml'), pyproject)
1349
1346
  utils.write(os.path.join(self.__base_dir, 'Pipfile'), pipfile)
1350
1347
  utils.write(os.path.join(self.__base_dir, 'README.md'), readme)
1351
1348
  utils.write(os.path.join(self.__base_dir, '.gitignore'), git_ignore)
@@ -0,0 +1,55 @@
1
+ # Package for PostgreSQL {dbname} database.
2
+ # You can edit the following parameters:
3
+ # - version (in {package_name}/version.txt)
4
+ # - authors
5
+ # - license
6
+ # - keywords
7
+ # - description
8
+
9
+ [build-system]
10
+ requires = ["setuptools>=61.0", "wheel"]
11
+ build-backend = "setuptools.build_meta"
12
+
13
+ [project]
14
+ name = "{package_name}"
15
+ dynamic = ["version"]
16
+ description = "Package for {dbname} PostgreSQL database"
17
+ readme = "README.md"
18
+ keywords = []
19
+ authors = [
20
+ {{name = "Your Name", email = "your.email@example.com"}}
21
+ ]
22
+ license = "MIT"
23
+ classifiers = [
24
+ # How mature is this project? Common values are
25
+ # 3 - Alpha
26
+ # 4 - Beta
27
+ # 5 - Production/Stable
28
+ "Development Status :: 3 - Alpha",
29
+
30
+ # Indicate who your project is intended for
31
+ "Intended Audience :: Developers",
32
+ "Topic :: Software Development :: Build Tools",
33
+
34
+ # Specify the Python versions you support here
35
+ "Programming Language :: Python :: 3.9",
36
+ "Programming Language :: Python :: 3.10",
37
+ "Programming Language :: Python :: 3.11",
38
+ "Programming Language :: Python :: 3.12",
39
+ "Programming Language :: Python :: 3.13",
40
+ "Programming Language :: Python :: 3.14",
41
+ ]
42
+ requires-python = ">=3.9"
43
+ dependencies = [
44
+ "half_orm=={half_orm_version}",
45
+ ]
46
+
47
+ [project.urls]
48
+ Homepage = ""
49
+
50
+ [tool.setuptools.packages.find]
51
+ where = ["."]
52
+ exclude = ["contrib", "docs", "tests", "patches", "svg"]
53
+
54
+ [tool.setuptools.dynamic]
55
+ version = {{file = "{package_name}/version.txt"}}
@@ -0,0 +1 @@
1
+ 0.17.0-a11
@@ -1,42 +1,31 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 0.17.0a10
3
+ Version: 0.17.0a11
4
4
  Summary: half_orm development Framework.
5
- Home-page: https://github.com/collorg/halfORM_dev
6
- Author: Joël Maïzi
7
- Author-email: joel.maizi@collorg.org
8
- License: GNU General Public License v3 (GPLv3)
5
+ Author-email: Joël Maïzi <joel.maizi@collorg.org>
6
+ License-Expression: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/collorg/halfORM_dev
9
8
  Keywords: postgres,relation-object mapping
10
9
  Classifier: Development Status :: 3 - Alpha
11
10
  Classifier: Intended Audience :: Developers
12
11
  Classifier: Topic :: Software Development :: Build Tools
13
- Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
14
- Classifier: Programming Language :: Python :: 3.8
15
12
  Classifier: Programming Language :: Python :: 3.9
16
13
  Classifier: Programming Language :: Python :: 3.10
17
14
  Classifier: Programming Language :: Python :: 3.11
18
15
  Classifier: Programming Language :: Python :: 3.12
19
16
  Classifier: Programming Language :: Python :: 3.13
20
17
  Classifier: Programming Language :: Python :: 3.14
18
+ Requires-Python: >=3.9
21
19
  Description-Content-Type: text/markdown
22
20
  License-File: LICENSE
23
21
  License-File: AUTHORS
24
22
  Requires-Dist: GitPython
25
23
  Requires-Dist: click
26
24
  Requires-Dist: pydash
27
- Requires-Dist: half_orm<0.18.0,>=0.17.0
28
25
  Requires-Dist: pytest
29
- Dynamic: author
30
- Dynamic: author-email
31
- Dynamic: classifier
32
- Dynamic: description
33
- Dynamic: description-content-type
34
- Dynamic: home-page
35
- Dynamic: keywords
36
- Dynamic: license
26
+ Requires-Dist: half_orm<0.18.0,>=0.17.0
37
27
  Dynamic: license-file
38
28
  Dynamic: requires-dist
39
- Dynamic: summary
40
29
 
41
30
  # half_orm_dev
42
31
 
@@ -1,6 +1,7 @@
1
1
  AUTHORS
2
2
  LICENSE
3
3
  README.md
4
+ pyproject.toml
4
5
  setup.py
5
6
  half_orm_dev/__init__.py
6
7
  half_orm_dev/cli_extension.py
@@ -52,11 +53,9 @@ half_orm_dev/templates/init_module_template
52
53
  half_orm_dev/templates/module_template_1
53
54
  half_orm_dev/templates/module_template_2
54
55
  half_orm_dev/templates/module_template_3
56
+ half_orm_dev/templates/pyproject.toml
55
57
  half_orm_dev/templates/relation_test
56
- half_orm_dev/templates/setup.py
57
58
  half_orm_dev/templates/sql_adapter
58
59
  half_orm_dev/templates/warning
59
60
  half_orm_dev/templates/git-hooks/pre-commit
60
- half_orm_dev/templates/git-hooks/prepare-commit-msg
61
- tests/__init__.py
62
- tests/conftest.py
61
+ half_orm_dev/templates/git-hooks/prepare-commit-msg
@@ -0,0 +1,54 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "half_orm_dev"
7
+ dynamic = ["version", "dependencies"]
8
+ description = "half_orm development Framework."
9
+ readme = "README.md"
10
+ keywords = ["postgres", "relation-object mapping"]
11
+ authors = [
12
+ {name = "Joël Maïzi", email = "joel.maizi@collorg.org"}
13
+ ]
14
+ license = "GPL-3.0-or-later"
15
+ classifiers = [
16
+ # How mature is this project? Common values are
17
+ # 3 - Alpha
18
+ # 4 - Beta
19
+ # 5 - Production/Stable
20
+ "Development Status :: 3 - Alpha",
21
+
22
+ # Indicate who your project is intended for
23
+ "Intended Audience :: Developers",
24
+ "Topic :: Software Development :: Build Tools",
25
+
26
+ # Specify the Python versions you support here
27
+ "Programming Language :: Python :: 3.9",
28
+ "Programming Language :: Python :: 3.10",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Programming Language :: Python :: 3.12",
31
+ "Programming Language :: Python :: 3.13",
32
+ "Programming Language :: Python :: 3.14",
33
+ ]
34
+ requires-python = ">=3.9"
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/collorg/halfORM_dev"
38
+
39
+ [tool.setuptools.packages.find]
40
+ where = ["."]
41
+ include = ["half_orm_dev*"]
42
+
43
+ [tool.setuptools.package-data]
44
+ half_orm_dev = [
45
+ "templates/*",
46
+ "templates/git-hooks/*",
47
+ "templates/.gitignore",
48
+ "db_patch_system/*",
49
+ "patches/**/*",
50
+ "version.txt"
51
+ ]
52
+
53
+ [tool.setuptools.dynamic]
54
+ version = {file = "half_orm_dev/version.txt"}
@@ -0,0 +1,49 @@
1
+ #-*- coding: utf-8 -*-
2
+ """
3
+ Minimal setup.py for dynamic half_orm dependency calculation.
4
+
5
+ Most configuration is in pyproject.toml. This file exists only to add
6
+ the dynamically calculated half_orm version constraint.
7
+ """
8
+
9
+ import re
10
+ from pathlib import Path
11
+ from setuptools import setup
12
+
13
+
14
+ def get_half_orm_version_constraint():
15
+ """
16
+ Calculate half_orm version constraint from half_orm_dev version.
17
+
18
+ For version X.Y.Z[-xxx], returns: half_orm>=X.Y.0,<X.(Y+1).0
19
+ """
20
+ version_file = Path(__file__).parent / "half_orm_dev" / "version.txt"
21
+ version_text = version_file.read_text(encoding="utf-8").strip()
22
+
23
+ # Parse version with regex to handle X.Y.Z[-suffix]
24
+ match = re.match(r'^(\d+)\.(\d+)\.(\d+)(?:-.*)?$', version_text)
25
+
26
+ if not match:
27
+ raise ValueError(f"Invalid version format in version.txt: {version_text}")
28
+
29
+ major, minor, patch = match.groups()
30
+ major, minor = int(major), int(minor)
31
+
32
+ # Generate constraint: half_orm>=X.Y.0,<X.(Y+1).0
33
+ min_version = f"{major}.{minor}.0"
34
+ max_version = f"{major}.{minor + 1}.0"
35
+
36
+ return f"half_orm>={min_version},<{max_version}"
37
+
38
+
39
+ # Call setup with all dependencies (including dynamic half_orm constraint)
40
+ # All other configuration is in pyproject.toml
41
+ setup(
42
+ install_requires=[
43
+ "GitPython",
44
+ "click",
45
+ "pydash",
46
+ "pytest",
47
+ get_half_orm_version_constraint(),
48
+ ]
49
+ )
@@ -1,81 +0,0 @@
1
- """Package for PostgreSQL {dbname} database.
2
- You can edit the following parameters:
3
- - version
4
- - author
5
- - author_email
6
- - license
7
- - keywords
8
- """
9
-
10
- from setuptools import setup, find_packages
11
- from codecs import open
12
- from os import path
13
- import re
14
- import half_orm
15
-
16
- PWD = path.abspath(path.dirname(__file__))
17
-
18
- def get_long_description():
19
- """
20
- Return the README.
21
- """
22
- with open("README.md", encoding="utf8") as f:
23
- return f.read()
24
-
25
- def get_version(package):
26
- """
27
- Return package version as listed in `__version__` in `init.py`.
28
- """
29
- with open(path.join(package, "version.txt")) as f:
30
- return f.read()
31
-
32
-
33
- package_name='{package_name}'
34
-
35
- setup(
36
- name=package_name,
37
-
38
- version=get_version(package_name),
39
-
40
- description='Package for {dbname} PG',
41
- long_description=get_long_description(),
42
- long_description_content_type='text/markdown',
43
- url='',
44
-
45
- author='',
46
- author_email='',
47
- license='',
48
-
49
- # See https://pypi.python.org/pypi?%3Aaction=list_classifiers
50
- classifiers=[
51
- # How mature is this project? Common values are
52
- # 3 - Alpha
53
- # 4 - Beta
54
- # 5 - Production/Stable
55
- 'Development Status :: 3 - Alpha',
56
-
57
- # Indicate who your project is intended for
58
- 'Intended Audience :: Developers',
59
- 'Topic :: Software Development :: Build Tools',
60
-
61
- # Pick your license as you wish (should match "license" above)
62
- 'License :: OSI Approved :: MIT License',
63
-
64
- # Specify the Python versions you support here. In particular, ensure
65
- # that you indicate whether you support Python 2, Python 3 or both.
66
- 'Programming Language :: Python :: 3.6',
67
- 'Programming Language :: Python :: 3.7',
68
- 'Programming Language :: Python :: 3.8',
69
- 'Programming Language :: Python :: 3.9',
70
- 'Programming Language :: Python :: 3.10',
71
- ],
72
-
73
- keywords='',
74
-
75
- packages=find_packages(exclude=['contrib', 'docs', 'tests', 'patches', 'svg']),
76
-
77
- install_requires=[
78
- 'half_orm=={half_orm_version}'
79
- ],
80
-
81
- )
@@ -1 +0,0 @@
1
- 0.17.0-a10
@@ -1,80 +0,0 @@
1
- #-*- coding: utf-8 -*-
2
-
3
- import os
4
- import codecs
5
- import re
6
- from setuptools import setup, find_packages
7
-
8
- def read(name):
9
- return codecs.open(
10
- os.path.join(os.path.dirname(__file__), name), "r", "utf-8").read()
11
-
12
- def get_half_orm_version_constraint():
13
- """
14
- Calculate half_orm version constraint from half_orm_dev version.
15
-
16
- For version X.Y.Z[-xxx], returns: half_orm>=X.Y.0,<X.(Y+1).0
17
- """
18
- version_text = read('half_orm_dev/version.txt').strip()
19
-
20
- # Parse version with regex to handle X.Y.Z[-suffix]
21
- match = re.match(r'^(\d+)\.(\d+)\.(\d+)(?:-.*)?$', version_text)
22
-
23
- if not match:
24
- raise ValueError(f"Invalid version format in version.txt: {version_text}")
25
-
26
- major, minor, patch = match.groups()
27
- major, minor = int(major), int(minor)
28
-
29
- # Generate constraint: half_orm>=X.Y.0,<X.(Y+1).0
30
- min_version = f"{major}.{minor}.0"
31
- max_version = f"{major}.{minor + 1}.0"
32
-
33
- return f"half_orm>={min_version},<{max_version}"
34
-
35
- setup(
36
- name='half_orm_dev',
37
- version=read('half_orm_dev/version.txt').strip(),
38
- description="half_orm development Framework.",
39
- long_description=read('README.md'),
40
- keywords='postgres, relation-object mapping',
41
- author='Joël Maïzi',
42
- author_email='joel.maizi@collorg.org',
43
- url='https://github.com/collorg/halfORM_dev',
44
- license='GNU General Public License v3 (GPLv3)',
45
- packages=find_packages(),
46
- package_data={'half_orm_dev': [
47
- 'templates/*', 'templates/git-hooks/*', 'templates/.gitignore', 'db_patch_system/*', 'patches/**/*', 'version.txt']},
48
- install_requires=[
49
- 'GitPython',
50
- 'click',
51
- 'pydash',
52
- get_half_orm_version_constraint(),
53
- 'pytest'
54
- ],
55
- classifiers=[
56
- # How mature is this project? Common values are
57
- # 3 - Alpha
58
- # 4 - Beta
59
- # 5 - Production/Stable
60
- 'Development Status :: 3 - Alpha',
61
-
62
- # Indicate who your project is intended for
63
- 'Intended Audience :: Developers',
64
- 'Topic :: Software Development :: Build Tools',
65
-
66
- # Pick your license as you wish (should match "license" above)
67
- 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
68
-
69
- # Specify the Python versions you support here. In particular, ensure
70
- # that you indicate whether you support Python 2, Python 3 or both.
71
- 'Programming Language :: Python :: 3.8',
72
- 'Programming Language :: Python :: 3.9',
73
- 'Programming Language :: Python :: 3.10',
74
- 'Programming Language :: Python :: 3.11',
75
- 'Programming Language :: Python :: 3.12',
76
- 'Programming Language :: Python :: 3.13',
77
- 'Programming Language :: Python :: 3.14',
78
- ],
79
- long_description_content_type = "text/markdown"
80
- )
File without changes
@@ -1,335 +0,0 @@
1
- """
2
- Shared pytest fixtures for half_orm_dev tests.
3
- """
4
-
5
- import pytest
6
- import tempfile
7
- import shutil
8
- from pathlib import Path
9
- from unittest.mock import Mock, patch
10
- from half_orm_dev.database import Database
11
- from half_orm_dev.repo import Repo
12
-
13
-
14
- @pytest.fixture
15
- def temp_repo():
16
- """
17
- Create temporary directory structure for testing.
18
- """
19
- temp_dir = tempfile.mkdtemp()
20
- patches_dir = Path(temp_dir) / "Patches"
21
- patches_dir.mkdir()
22
-
23
- # Create releases/ directory with candidates file (for new workflow)
24
- releases_dir = Path(temp_dir) / "releases"
25
- releases_dir.mkdir(exist_ok=True)
26
- candidates_file = releases_dir / "0.17.0-candidates.txt"
27
- candidates_file.write_text("", encoding='utf-8') # Empty file initially
28
-
29
- repo = Mock()
30
- repo.base_dir = temp_dir
31
- repo.devel = True
32
- repo.name = "test_database"
33
- repo.git_origin = "https://github.com/test/repo.git"
34
-
35
- # Create default HGit mock with tag methods
36
- mock_hgit = Mock()
37
- mock_hgit.fetch_tags = Mock()
38
- mock_hgit.tag_exists = Mock(return_value=False)
39
- mock_hgit.create_tag = Mock()
40
- mock_hgit.push_tag = Mock()
41
- repo.hgit = mock_hgit
42
-
43
- yield repo, temp_dir, patches_dir
44
-
45
- shutil.rmtree(temp_dir)
46
-
47
-
48
- @pytest.fixture
49
- def patch_manager(temp_repo):
50
- """
51
- Create PatchManager instance with temporary repo.
52
-
53
- The repo.hgit already has default tag mocks configured in temp_repo fixture.
54
- Tests can override repo.hgit if needed, but should use mock_hgit_complete fixture.
55
- """
56
- from half_orm_dev.patch_manager import PatchManager
57
-
58
- repo, temp_dir, patches_dir = temp_repo
59
- repo.restore_database_from_schema = Repo.restore_database_from_schema.__get__(repo, type(repo))
60
- mock_get_version = Mock(return_value=(16, 1))
61
- repo.database.get_postgres_version = mock_get_version
62
- patch_mgr = PatchManager(repo)
63
- return patch_mgr, repo, temp_dir, patches_dir
64
-
65
-
66
- @pytest.fixture
67
- def mock_hgit_complete():
68
- """
69
- Create complete mock HGit for create_patch workflow tests.
70
-
71
- Provides all necessary mocks for successful patch creation workflow:
72
- - Branch validation (on ho-release/X.Y.Z) - NEW workflow
73
- - Repository clean check
74
- - Remote configuration check
75
- - Remote fetch operations
76
- - Branch synchronization check
77
- - Tag availability check
78
- - Git operations (checkout, add, commit, tag, push)
79
- - Branch operations (create, delete, checkout)
80
- """
81
- mock_hgit = Mock()
82
-
83
- # Branch and repo state - NEW: use ho-release/X.Y.Z instead of ho-prod
84
- mock_hgit.branch = "ho-release/0.17.0"
85
- mock_hgit.repos_is_clean.return_value = True
86
- mock_hgit.has_remote.return_value = True
87
-
88
- # Branch synchronization check
89
- # Returns (is_synced, status) tuple
90
- mock_hgit.is_branch_synced.return_value = (True, "synced")
91
-
92
- # Fetch operations
93
- mock_hgit.fetch_from_origin.return_value = None
94
- mock_hgit.fetch_tags.return_value = None
95
-
96
- # Tag operations
97
- mock_hgit.tag_exists.return_value = False # No existing tags
98
- mock_hgit.create_tag.return_value = None
99
- mock_hgit.push_tag.return_value = None
100
- mock_hgit.delete_local_tag.return_value = None
101
-
102
- # Branch operations
103
- mock_hgit.checkout.return_value = None
104
- mock_hgit.delete_local_branch.return_value = None
105
- mock_hgit.push_branch.return_value = None
106
-
107
- # Git proxy methods
108
- mock_hgit.add.return_value = None
109
- mock_hgit.commit.return_value = None
110
-
111
- # Git repo access for reset operations
112
- mock_git_repo = Mock()
113
- mock_git_repo.git.reset.return_value = None
114
- mock_hgit._HGit__git_repo = mock_git_repo
115
-
116
- return mock_hgit
117
-
118
- @pytest.fixture
119
- def sample_patch_files():
120
- """
121
- Provide sample patch file contents for testing.
122
- """
123
- return {
124
- '01_create_table.sql': 'CREATE TABLE users (id SERIAL PRIMARY KEY);',
125
- '02_add_indexes.sql': 'CREATE INDEX idx_users_id ON users(id);',
126
- 'migrate.py': 'print("Running migration")',
127
- 'cleanup.py': 'print("Cleanup complete")',
128
- }
129
-
130
-
131
- @pytest.fixture
132
- def mock_database():
133
- """
134
- Mock database connection for testing.
135
-
136
- Provides a mock database connection object that can be used
137
- to test SQL execution without requiring a real database.
138
-
139
- Returns:
140
- Mock: Mock database connection with execute_query method
141
- """
142
- mock_db = Mock()
143
- mock_db.execute_query = Mock()
144
- mock_db.execute = Mock()
145
- mock_db.cursor = Mock()
146
-
147
- return mock_db
148
-
149
- @pytest.fixture
150
- def mock_database_for_schema_generation():
151
- """
152
- Create complete Database mock for _generate_schema_sql() testing.
153
-
154
- Provides a mock with all necessary attributes and methods for schema
155
- generation tests, including mangled private attributes.
156
-
157
- Returns:
158
- Mock: Configured Database mock with:
159
- - _Database__name: Database name (mangled private attribute)
160
- - _collect_connection_params(): Returns connection parameters
161
- - _get_connection_params(): Returns connection parameters
162
- - _execute_pg_command(): Mock for pg_dump execution
163
-
164
- Example:
165
- def test_something(self, mock_database_for_schema_generation, tmp_path):
166
- database = mock_database_for_schema_generation
167
- model_dir = tmp_path / "model"
168
- model_dir.mkdir()
169
-
170
- result = Database._generate_schema_sql(database, "1.0.0", model_dir)
171
- """
172
- mock_db = Mock(spec=Database)
173
-
174
- # Set mangled private attribute for database name
175
- mock_db._Database__name = "test_db"
176
-
177
- # Mock connection parameter methods
178
- connection_params = {
179
- 'user': 'test_user',
180
- 'password': 'test_pass',
181
- 'host': 'localhost',
182
- 'port': 5432,
183
- 'production': False
184
- }
185
-
186
- mock_db._collect_connection_params = Mock(return_value=connection_params)
187
- mock_db._get_connection_params = Mock(return_value=connection_params)
188
-
189
- # Mock pg_dump execution
190
- mock_db._execute_pg_command = Mock()
191
-
192
- return mock_db
193
-
194
- """
195
- Additional fixtures for ReleaseManager tests.
196
- Add these to tests/conftest.py
197
- """
198
-
199
- @pytest.fixture
200
- def mock_release_manager_basic(tmp_path):
201
- """
202
- Create basic ReleaseManager with minimal mocks.
203
-
204
- Provides:
205
- - ReleaseManager instance
206
- - Mock repo with base_dir
207
- - Temporary releases/ directory
208
-
209
- Returns:
210
- Tuple of (release_mgr, mock_repo, tmp_path)
211
- """
212
- from unittest.mock import Mock
213
- from half_orm_dev.release_manager import ReleaseManager
214
-
215
- mock_repo = Mock()
216
- mock_repo.base_dir = str(tmp_path)
217
-
218
- # Create releases/ directory
219
- releases_dir = tmp_path / "releases"
220
- releases_dir.mkdir(exist_ok=True)
221
-
222
- release_mgr = ReleaseManager(mock_repo)
223
-
224
- return release_mgr, mock_repo, tmp_path
225
-
226
-
227
- @pytest.fixture
228
- def mock_release_manager_with_hgit(mock_release_manager_basic):
229
- """
230
- Create ReleaseManager with fully mocked HGit.
231
-
232
- Provides:
233
- - ReleaseManager instance
234
- - Mock repo with HGit configured
235
- - HGit mocked for all Git operations
236
- - Default "happy path" configuration
237
-
238
- Returns:
239
- Tuple of (release_mgr, mock_repo, mock_hgit, tmp_path)
240
- """
241
- from unittest.mock import Mock
242
-
243
- release_mgr, mock_repo, tmp_path = mock_release_manager_basic
244
-
245
- # Mock HGit with all required methods
246
- mock_hgit = Mock()
247
-
248
- # Branch and repo state
249
- mock_hgit.branch = "ho-prod"
250
- mock_hgit.repos_is_clean.return_value = True
251
-
252
- # Fetch and sync
253
- mock_hgit.fetch_from_origin.return_value = None
254
- mock_hgit.is_branch_synced.return_value = (True, "synced")
255
- mock_hgit.pull.return_value = None
256
-
257
- # Git operations
258
- mock_hgit.add.return_value = None
259
- mock_hgit.commit.return_value = None
260
- mock_hgit.push.return_value = None
261
-
262
- mock_repo.hgit = mock_hgit
263
-
264
- return release_mgr, mock_repo, mock_hgit, tmp_path
265
-
266
-
267
- @pytest.fixture
268
- def mock_release_manager_with_production(mock_release_manager_with_hgit):
269
- """
270
- Create ReleaseManager with production version mocking.
271
-
272
- Provides:
273
- - Everything from mock_release_manager_with_hgit
274
- - Mock _get_production_version() to return "1.3.5"
275
- - Mock model/schema.sql symlink (for tests that directly test _get_production_version)
276
- - Default production version: 1.3.5
277
-
278
- Returns:
279
- Tuple of (release_mgr, mock_repo, mock_hgit, tmp_path, prod_version)
280
- """
281
- from unittest.mock import Mock, patch
282
-
283
- release_mgr, mock_repo, mock_hgit, tmp_path = mock_release_manager_with_hgit
284
-
285
- # Create model/ directory with schema files (for tests that test _get_production_version directly)
286
- model_dir = tmp_path / "model"
287
- model_dir.mkdir()
288
-
289
- # Create versioned schema file
290
- prod_version = "1.3.5"
291
- schema_file = model_dir / f"schema-{prod_version}.sql"
292
- schema_file.write_text("-- Schema version 1.3.5")
293
-
294
- # Create symlink schema.sql -> schema-1.3.5.sql
295
- schema_symlink = model_dir / "schema.sql"
296
- schema_symlink.symlink_to(f"schema-{prod_version}.sql")
297
-
298
- # Mock database last_release_s
299
- mock_database = Mock()
300
- mock_database.last_release_s = prod_version
301
- mock_repo.database = mock_database
302
-
303
- # CRITICAL: Mock _get_production_version() to avoid reading symlink in most tests
304
- # This allows tests to focus on workflow without setting up full file structure
305
- release_mgr._get_production_version = Mock(return_value=prod_version)
306
-
307
- return release_mgr, mock_repo, mock_hgit, tmp_path, prod_version
308
-
309
-
310
- @pytest.fixture
311
- def sample_release_files(tmp_path):
312
- """
313
- Create sample release files in releases/ directory.
314
-
315
- Creates:
316
- - releases/1.3.4.txt (production)
317
- - releases/1.3.5-rc2.txt (rc)
318
- - releases/1.4.0-stage.txt (stage)
319
-
320
- Returns:
321
- Tuple of (releases_dir, dict of created files)
322
- """
323
- releases_dir = tmp_path / "releases"
324
- releases_dir.mkdir(exist_ok=True)
325
-
326
- files = {
327
- '1.3.4.txt': '',
328
- '1.3.5-rc2.txt': '',
329
- '1.4.0-stage.txt': '',
330
- }
331
-
332
- for filename, content in files.items():
333
- (releases_dir / filename).write_text(content)
334
-
335
- return releases_dir, files
@@ -1,5 +1,5 @@
1
1
  GitPython
2
2
  click
3
3
  pydash
4
- half_orm<0.18.0,>=0.17.0
5
4
  pytest
5
+ half_orm<0.18.0,>=0.17.0