half-orm-dev 1.0.0a4__tar.gz → 1.0.0a5__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 (78) hide show
  1. {half_orm_dev-1.0.0a4/half_orm_dev.egg-info → half_orm_dev-1.0.0a5}/PKG-INFO +1 -1
  2. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/modules.py +5 -20
  3. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/release_manager.py +35 -0
  4. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/repo.py +7 -2
  5. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/module_template_1 +3 -0
  6. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/module_template_3 +4 -1
  7. half_orm_dev-1.0.0a5/half_orm_dev/version.txt +1 -0
  8. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5/half_orm_dev.egg-info}/PKG-INFO +1 -1
  9. half_orm_dev-1.0.0a4/half_orm_dev/version.txt +0 -1
  10. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/AUTHORS +0 -0
  11. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/LICENSE +0 -0
  12. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/README.md +0 -0
  13. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/__init__.py +0 -0
  14. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/bootstrap_manager.py +0 -0
  15. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/__init__.py +0 -0
  16. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/__init__.py +0 -0
  17. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/apply.py +0 -0
  18. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/bootstrap.py +0 -0
  19. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/check.py +0 -0
  20. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/clone.py +0 -0
  21. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/init.py +0 -0
  22. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/migrate.py +0 -0
  23. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/patch.py +0 -0
  24. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/release.py +0 -0
  25. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/restore.py +0 -0
  26. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/revert_migration.py +0 -0
  27. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/set_git_origin.py +0 -0
  28. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/sync.py +0 -0
  29. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/todo.py +0 -0
  30. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/undo.py +0 -0
  31. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/update.py +0 -0
  32. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/commands/upgrade.py +0 -0
  33. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli/main.py +0 -0
  34. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/cli_extension.py +0 -0
  35. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/database.py +0 -0
  36. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/decorators.py +0 -0
  37. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/file_executor.py +0 -0
  38. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/hgit.py +0 -0
  39. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/migration_manager.py +0 -0
  40. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/migrations/0/17/1/00_move_to_hop.py +0 -0
  41. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/migrations/0/17/1/01_txt_to_toml.py +0 -0
  42. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/migrations/0/17/4/00_toml_dict_format.py +0 -0
  43. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/migrations/0/17/4/01_add_bootstrap_table.py +0 -0
  44. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/migrations/0/17/4/02_move_patches_to_subdirs.py +0 -0
  45. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/migrations/0/17/5/01_update_pyproject_dependency.py +0 -0
  46. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/migrations/0/18/0/00_add_async_support.py +0 -0
  47. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/migrations/0/18/0/01_update_default_tests.py +0 -0
  48. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/migrations/hop/BREAKING_CHANGES-1.0.0.md +0 -0
  49. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/patch_manager.py +0 -0
  50. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/patch_validator.py +0 -0
  51. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +0 -0
  52. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +0 -0
  53. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +0 -0
  54. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/patches/log +0 -0
  55. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/patches/sql/half_orm_meta.sql +0 -0
  56. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/release_file.py +0 -0
  57. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/scripts/repair-metadata.py +0 -0
  58. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/.gitignore +0 -0
  59. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/MANIFEST.in +0 -0
  60. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/README +0 -0
  61. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/conftest_template +0 -0
  62. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/git-hooks/pre-commit +0 -0
  63. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/git-hooks/prepare-commit-msg +0 -0
  64. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/init_module_template +0 -0
  65. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/module_template_2 +0 -0
  66. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/pyproject.toml +0 -0
  67. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/relation_test +0 -0
  68. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/sql_adapter +0 -0
  69. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/templates/warning +0 -0
  70. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev/utils.py +0 -0
  71. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev.egg-info/SOURCES.txt +0 -0
  72. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev.egg-info/dependency_links.txt +0 -0
  73. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev.egg-info/entry_points.txt +0 -0
  74. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev.egg-info/requires.txt +0 -0
  75. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/half_orm_dev.egg-info/top_level.txt +0 -0
  76. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/pyproject.toml +0 -0
  77. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/setup.cfg +0 -0
  78. {half_orm_dev-1.0.0a4 → half_orm_dev-1.0.0a5}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 1.0.0a4
3
+ Version: 1.0.0a5
4
4
  Summary: half_orm development Framework.
5
5
  Author-email: Joël Maïzi <joel.maizi@collorg.org>
6
6
  License-Expression: GPL-3.0-or-later
@@ -304,8 +304,7 @@ def __gen_typedict(relation, fkeys) -> list:
304
304
  """Generate TypedDict class(es) for a relation.
305
305
 
306
306
  Returns a list of class strings: nested JSON TypedDicts first, then the main class.
307
- FK fields starting with 'rfk_' (reverse) are typed List['TargetDict'];
308
- all other FK fields are typed 'TargetDict'.
307
+ Only database columns are included FK accessor attributes are not part of a row dict.
309
308
  json/jsonb fields with a json_schema generate nested TypedDict classes.
310
309
  """
311
310
  rel = relation()
@@ -332,24 +331,6 @@ def __gen_typedict(relation, fkeys) -> list:
332
331
  line = f"# {line} # FIX ME! {error}"
333
332
  fields.append(line)
334
333
 
335
- aliases = {constraint: alias for alias, constraint in fkeys.items() if alias != ''}
336
- for constraint_name, fkey in rel._ho_fkeys.items():
337
- if constraint_name in aliases:
338
- attr_name = aliases[constraint_name]
339
- elif constraint_name.startswith('_reverse_fkey_'):
340
- attr_name = 'rfk_' + constraint_name[len('_reverse_fkey_'):]
341
- else:
342
- attr_name = 'fk_' + constraint_name
343
- try:
344
- fk_fqrn = list(fkey()._t_fqrn)
345
- target_name = f'{__get_full_class_name(fk_fqrn[1], fk_fqrn[2])}Dict'
346
- except Exception:
347
- target_name = dict_class_name
348
- if attr_name.startswith('rfk_'):
349
- fields.append(f" {attr_name}: Optional[List['{target_name}']]")
350
- else:
351
- fields.append(f" {attr_name}: Optional['{target_name}']")
352
-
353
334
  body = '\n'.join(fields) if fields else ' pass'
354
335
  main_class = f'class {dict_class_name}(TypedDict, total=False):\n{body}'
355
336
  return extra_classes + [main_class]
@@ -521,6 +502,9 @@ def __update_this_module(
521
502
  inheritance_import, inherited_classes = __get_inheritance_info(
522
503
  rel, package_name)
523
504
 
505
+ t_qrn = list(rel._t_fqrn)[1:]
506
+ dict_class_name = f'{__get_full_class_name(*t_qrn)}Dict'
507
+
524
508
  # Generate Python module
525
509
  with open(module_path, 'w', encoding='utf-8') as file_:
526
510
  documentation = "\n".join([line and f" {line}" or "" for line in str(rel).split("\n")[1:]])
@@ -534,6 +518,7 @@ def __update_this_module(
534
518
  inherited_classes=inherited_classes,
535
519
  class_name=class_name,
536
520
  dc_name=rel._ho_dataclass_name(),
521
+ dict_class_name=dict_class_name,
537
522
  fqtn=fqtn,
538
523
  kwargs=kwargs,
539
524
  arg_names=arg_names,
@@ -2497,6 +2497,12 @@ class ReleaseManager:
2497
2497
 
2498
2498
  # Save original branch for rollback
2499
2499
  original_branch = self._repo.hgit.branch
2500
+ # Save schema.sql symlink target so rollback can restore it if
2501
+ # _generate_schema_sql updates it before a failure.
2502
+ _schema_sql = Path(self._repo.model_dir) / 'schema.sql'
2503
+ original_schema_link = (
2504
+ os.readlink(str(_schema_sql)) if _schema_sql.is_symlink() else None
2505
+ )
2500
2506
 
2501
2507
  try:
2502
2508
  # 5. Create temporary branch from release branch (validation only)
@@ -2547,6 +2553,15 @@ class ReleaseManager:
2547
2553
  staged_patches = release_file.get_patches(status="staged")
2548
2554
  self._create_prod_snapshot(version, staged_patches, release_file, model_dir)
2549
2555
 
2556
+ # 10b. Passe 2: validate all bootstrap scripts against final schema.
2557
+ # schema.sql now points to schema-X.Y.Z.sql (just generated).
2558
+ # restore_database_from_schema() resets the DB and runs ALL pending
2559
+ # bootstrap scripts in order — exactly what a fresh clone does.
2560
+ # Any failure here aborts promotion before the commit.
2561
+ print(f"\n🔍 Validating bootstrap scripts against schema {version}...")
2562
+ self._repo.restore_database_from_schema()
2563
+ print(f"✓ Bootstrap validation passed")
2564
+
2550
2565
  # 11. Commit the merge with all promote changes included
2551
2566
  self._repo.hgit.add(".")
2552
2567
  self._repo.hgit.commit("-m", commit_msg)
@@ -2625,6 +2640,15 @@ class ReleaseManager:
2625
2640
  except GitCommandError:
2626
2641
  pass
2627
2642
 
2643
+ # Restore tracked files deleted/modified outside of git control
2644
+ # during the merge window (by _generate_schema_sql and
2645
+ # _create_prod_snapshot). merge_abort resets the index but
2646
+ # not working-tree files that were deleted directly on disk.
2647
+ try:
2648
+ self._repo.hgit.checkout('HEAD', '--', '.')
2649
+ except GitCommandError:
2650
+ pass
2651
+
2628
2652
  # Return to original branch
2629
2653
  try:
2630
2654
  self._repo.hgit.checkout(original_branch)
@@ -2641,6 +2665,17 @@ class ReleaseManager:
2641
2665
  except GitCommandError:
2642
2666
  pass # Already deleted (prod validation complete) or never created
2643
2667
 
2668
+ # Restore schema.sql symlink if _generate_schema_sql changed it
2669
+ # before the failure (merge_abort does not undo symlink changes
2670
+ # made outside of the merge commit itself).
2671
+ if original_schema_link is not None:
2672
+ try:
2673
+ if _schema_sql.is_symlink() and os.readlink(str(_schema_sql)) != original_schema_link:
2674
+ _schema_sql.unlink()
2675
+ _schema_sql.symlink_to(original_schema_link)
2676
+ except OSError:
2677
+ pass
2678
+
2644
2679
  except (GitCommandError, TypeError) as cleanup_error:
2645
2680
  print(f" Warning: Cleanup failed: {cleanup_error}", file=sys.stderr)
2646
2681
 
@@ -868,12 +868,14 @@ class Repo:
868
868
 
869
869
  # Check if working directory is clean
870
870
  if git_repo.is_dirty(untracked_files=False):
871
+ status = git_repo.git.status('--short')
871
872
  raise RepoError(
872
873
  f"Working directory has uncommitted changes.\n"
873
874
  f"Please commit or stash your changes before running this command:\n"
874
875
  f" git stash\n"
875
876
  f" OR\n"
876
- f" git add . && git commit -m \"your message\""
877
+ f" git add . && git commit -m \"your message\"\n"
878
+ f"Dirty files:\n{status}"
877
879
  )
878
880
 
879
881
  # Switch to ho-prod temporarily
@@ -2494,11 +2496,14 @@ Each script is executed only once unless `--force` is used.
2494
2496
  # up_to_version prevents bootstraps from future versions from running.
2495
2497
  from half_orm_dev.bootstrap_manager import BootstrapManager
2496
2498
  bootstrap_mgr = BootstrapManager(self)
2497
- bootstrap_mgr.run_bootstrap(
2499
+ result = bootstrap_mgr.run_bootstrap(
2498
2500
  exclude_patch_id=exclude_bootstrap_patch_id,
2499
2501
  exclude_version=exclude_bootstrap_version,
2500
2502
  up_to_version=exclude_bootstrap_version
2501
2503
  )
2504
+ if result['errors']:
2505
+ error_msg = "\n".join([f" • {f}: {e}" for f, e in result['errors']])
2506
+ raise RepoError(f"Bootstrap execution failed:\n{error_msg}")
2502
2507
 
2503
2508
  except RepoError:
2504
2509
  # Re-raise RepoError as-is
@@ -6,7 +6,10 @@ WARNING!
6
6
 
7
7
  {warning}
8
8
  """
9
+ from typing import TYPE_CHECKING, Iterator
9
10
  from half_orm.model import register
10
11
  from {package_name} import MODEL, ho_dataclasses
12
+ if TYPE_CHECKING:
13
+ from {package_name}.ho_typeddicts import {dict_class_name}
11
14
  fields_aliases=None
12
15
 
@@ -1,3 +1,6 @@
1
1
  #pylint: disable=line-too-long, too-many-arguments, redefined-builtin, too-many-positional-arguments
2
2
  def __init__(self, {kwargs}):
3
- super().__init__({arg_names}, **kwargs)
3
+ super().__init__({arg_names}, **kwargs)
4
+
5
+ def __iter__(self) -> 'Iterator[{dict_class_name}]':
6
+ return super().__iter__()
@@ -0,0 +1 @@
1
+ 1.0.0-a5
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 1.0.0a4
3
+ Version: 1.0.0a5
4
4
  Summary: half_orm development Framework.
5
5
  Author-email: Joël Maïzi <joel.maizi@collorg.org>
6
6
  License-Expression: GPL-3.0-or-later
@@ -1 +0,0 @@
1
- 1.0.0-a4
File without changes
File without changes
File without changes
File without changes
File without changes