pypsbuilder 2.6.5__tar.gz → 2.6.7__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 (119) hide show
  1. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/.gitignore +3 -0
  2. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/.pre-commit-config.yaml +1 -1
  3. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/CHANGELOG.md +10 -1
  4. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/CONTRIBUTING.md +1 -1
  5. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/PKG-INFO +7 -7
  6. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/README.md +4 -4
  7. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/psbuilders-tutorial.rst +14 -9
  8. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pyproject.toml +2 -2
  9. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/__init__.py +1 -1
  10. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/tcinit.py +38 -6
  11. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/tests/test_tcinit.py +83 -6
  12. pypsbuilder-2.6.5/uv.lock +0 -4132
  13. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/.github/dependabot.yml +0 -0
  14. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/.github/workflows/release.yml +0 -0
  15. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/.github/workflows/testing.yml +0 -0
  16. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/.github/workflows/testrelease.yml +0 -0
  17. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/.readthedocs.yaml +0 -0
  18. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/.zenodo.json +0 -0
  19. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/AUTHORS.md +0 -0
  20. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/LICENSE +0 -0
  21. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/codecov.yml +0 -0
  22. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/_static/theme_overrides.css +0 -0
  23. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.psclasses.Dogmin.rst +0 -0
  24. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.psclasses.InvPoint.rst +0 -0
  25. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.psclasses.PTsection.rst +0 -0
  26. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.psclasses.PXsection.rst +0 -0
  27. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.psclasses.TCResult.rst +0 -0
  28. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.psclasses.TCResultSet.rst +0 -0
  29. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.psclasses.TXsection.rst +0 -0
  30. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.psclasses.UniLine.rst +0 -0
  31. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.psexplorer.PTPS.rst +0 -0
  32. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.psexplorer.PXPS.rst +0 -0
  33. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.psexplorer.TXPS.rst +0 -0
  34. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.tcapi.TC34API.rst +0 -0
  35. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api/pypsbuilder.tcapi.TC35API.rst +0 -0
  36. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/api.rst +0 -0
  37. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/authors.rst +0 -0
  38. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/command-line.rst +0 -0
  39. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/conf.py +0 -0
  40. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/create_environment.png +0 -0
  41. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/crossing.png +0 -0
  42. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/delta.png +0 -0
  43. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/install_locally.png +0 -0
  44. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/invsearch.png +0 -0
  45. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/isopleths_1.png +0 -0
  46. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/isopleths_2.png +0 -0
  47. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/modes.png +0 -0
  48. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psbuilder_areas.png +0 -0
  49. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psbuilder_dogmin.png +0 -0
  50. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psbuilder_finished.png +0 -0
  51. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psbuilder_highlight.png +0 -0
  52. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psbuilder_init.png +0 -0
  53. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psbuilder_inv.png +0 -0
  54. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psbuilder_modes.png +0 -0
  55. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psbuilder_uni.png +0 -0
  56. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psiso_alm.png +0 -0
  57. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psiso_labelkey.png +0 -0
  58. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psiso_mode.png +0 -0
  59. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psiso_other.png +0 -0
  60. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/psshow_out.png +0 -0
  61. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/ptpath.png +0 -0
  62. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/show_data.png +0 -0
  63. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/show_grid.png +0 -0
  64. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/show_label.png +0 -0
  65. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/show_out.png +0 -0
  66. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/show_plain.png +0 -0
  67. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/images/status.png +0 -0
  68. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/index.rst +0 -0
  69. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/psexplorer-tutorial.rst +0 -0
  70. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/docs/readme.rst +0 -0
  71. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/environment.yml +0 -0
  72. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/avgpelite/tc-avgpelite.txt +0 -0
  73. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/avgpelite/tc-ds62.txt +0 -0
  74. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/avgpelite/tc-mp50MnNCKFMASHTO.txt +0 -0
  75. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/avgpelite/tc-prefs.txt +0 -0
  76. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/avgpelite_34/tc-6axmn.txt +0 -0
  77. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/avgpelite_34/tc-avgpelite.txt +0 -0
  78. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/avgpelite_34/tc-ds62.txt +0 -0
  79. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/avgpelite_34/tc-prefs.txt +0 -0
  80. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/outputs/inv1-ic.txt +0 -0
  81. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/outputs/inv1-log.txt +0 -0
  82. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/outputs/inv2-ic.txt +0 -0
  83. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/outputs/inv2-log.txt +0 -0
  84. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/outputs/inv3-ic.txt +0 -0
  85. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/outputs/inv3-log.txt +0 -0
  86. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/outputs/uni1-ic.txt +0 -0
  87. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/outputs/uni1-log.txt +0 -0
  88. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/outputs/uni2-ic.txt +0 -0
  89. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/outputs/uni2-log.txt +0 -0
  90. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/outputs/uni3-ic.txt +0 -0
  91. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/examples/outputs/uni3-log.txt +0 -0
  92. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/notebooks/pypsbuilder.ipynb +0 -0
  93. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/images/ptbuilder.png +0 -0
  94. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/images/pxbuilder.png +0 -0
  95. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/images/pypsbuilder.png +0 -0
  96. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/images/txbuilder.png +0 -0
  97. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/psbuilders.py +0 -0
  98. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/psclasses.py +0 -0
  99. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/psexplorer.py +0 -0
  100. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/tcapi.py +0 -0
  101. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/tests/__init__.py +0 -0
  102. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/tests/test_pclasses.py +0 -0
  103. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/tests/test_psbuilders.py +0 -0
  104. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/tests/test_psclasses.py +0 -0
  105. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/tests/test_psexplorer.py +0 -0
  106. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/tests/test_tcapi.py +0 -0
  107. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui/addinv.ui +0 -0
  108. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui/adduni.ui +0 -0
  109. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui/compile_ui.sh +0 -0
  110. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui/ptbuilder.ui +0 -0
  111. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui/pxbuilder.ui +0 -0
  112. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui/txbuilder.ui +0 -0
  113. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui/uniguess.ui +0 -0
  114. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui_addinv.py +0 -0
  115. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui_adduni.py +0 -0
  116. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui_ptbuilder.py +0 -0
  117. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui_pxbuilder.py +0 -0
  118. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui_txbuilder.py +0 -0
  119. {pypsbuilder-2.6.5 → pypsbuilder-2.6.7}/pypsbuilder/ui_uniguess.py +0 -0
@@ -64,3 +64,6 @@ target/
64
64
 
65
65
  # venv
66
66
  .venv/
67
+
68
+ # uv lockfile (not committed for library project)
69
+ uv.lock
@@ -1,4 +1,4 @@
1
- default_stages: [commit, push]
1
+ default_stages: [pre-commit, pre-push]
2
2
  repos:
3
3
  - repo: https://github.com/astral-sh/ruff-pre-commit
4
4
  rev: v0.16.4
@@ -1,7 +1,16 @@
1
1
  # Changelog
2
2
  All notable pypsbuilder changes.
3
3
 
4
- ## [2.6.5] - master
4
+ ## [2.6.7] - 2026-08-29
5
+ ### Added
6
+ - `tcinit` now prompts for the P-T range (`[pmin, pmax, tmin, tmax]`), used to set the generated scriptfile's `diagramPT`/`calcP`/`calcT` lines
7
+ - `tcinit` creates the destination directory automatically when it doesn't exist yet but its parent does
8
+
9
+ ## [2.6.6] - 2026-08-29
10
+ ### Fixed
11
+ - project extra jupyter renamed to lab
12
+
13
+ ## [2.6.5] - 2026-08-29
5
14
  ### Added
6
15
  - `Import from JSON` menu item in ptbuilder/txbuilder/pxbuilder to batch-import invariant points and univariant lines JSON
7
16
  - THERMOCALC 3.51 support in `tcinit`
@@ -52,7 +52,7 @@ Ready to contribute? Here's how to set up `pypsbuilder` for local development.
52
52
 
53
53
  2. Set up a development environment with [uv](https://docs.astral.sh/uv/) and create a branch for local development. See the "Contributing / development install" section in [README.md](README.md) for the full command reference (running tests, formatting/linting with ruff, pre-commit hooks).
54
54
  ```
55
- $ uv sync --extra pyqt6 --extra jupyter --extra tests --extra docs --group dev
55
+ $ uv sync --group dev
56
56
  $ git checkout -b name-of-your-bugfix-or-feature
57
57
  ```
58
58
  Now you can make your changes locally.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pypsbuilder
3
- Version: 2.6.5
3
+ Version: 2.6.7
4
4
  Summary: THERMOCALC front-end for constructing and analyzing PT pseudosections
5
5
  Project-URL: Homepage, https://github.com/ondrolexa/pypsbuilder
6
6
  Project-URL: Documentation, https://pypsbuilder.readthedocs.io
@@ -52,8 +52,8 @@ Provides-Extra: docs
52
52
  Requires-Dist: sphinx; extra == 'docs'
53
53
  Requires-Dist: sphinx-mdinclude; extra == 'docs'
54
54
  Requires-Dist: sphinx-rtd-theme; extra == 'docs'
55
- Provides-Extra: jupyter
56
- Requires-Dist: jupyterlab; extra == 'jupyter'
55
+ Provides-Extra: lab
56
+ Requires-Dist: jupyterlab; extra == 'lab'
57
57
  Provides-Extra: pyqt5
58
58
  Requires-Dist: pyqt5; extra == 'pyqt5'
59
59
  Provides-Extra: pyqt6
@@ -83,9 +83,9 @@ Create an isolated environment and install **pypsbuilder** into it. **You should
83
83
  uv venv
84
84
  uv pip install pypsbuilder[pyqt6]
85
85
 
86
- JupyterLab could also be installed providing the `jupyter` option:
86
+ JupyterLab could also be installed providing the `lab` option:
87
87
 
88
- uv pip install pypsbuilder[pyqt6,jupyter]
88
+ uv pip install pypsbuilder[pyqt6,lab]
89
89
 
90
90
  ### Using pip and venv
91
91
 
@@ -116,9 +116,9 @@ and install **pypsbuilder** using pip within the environment. **You should choos
116
116
 
117
117
  pip install pypsbuilder[pyqt6]
118
118
 
119
- JupyterLab could also be installed providing the `jupyter` option:
119
+ JupyterLab could also be installed providing the `lab` option:
120
120
 
121
- pip install pypsbuilder[pyqt6,jupyter]
121
+ pip install pypsbuilder[pyqt6,lab]
122
122
 
123
123
  ## I'm using conda or mamba to manage environments
124
124
 
@@ -18,9 +18,9 @@ Create an isolated environment and install **pypsbuilder** into it. **You should
18
18
  uv venv
19
19
  uv pip install pypsbuilder[pyqt6]
20
20
 
21
- JupyterLab could also be installed providing the `jupyter` option:
21
+ JupyterLab could also be installed providing the `lab` option:
22
22
 
23
- uv pip install pypsbuilder[pyqt6,jupyter]
23
+ uv pip install pypsbuilder[pyqt6,lab]
24
24
 
25
25
  ### Using pip and venv
26
26
 
@@ -51,9 +51,9 @@ and install **pypsbuilder** using pip within the environment. **You should choos
51
51
 
52
52
  pip install pypsbuilder[pyqt6]
53
53
 
54
- JupyterLab could also be installed providing the `jupyter` option:
54
+ JupyterLab could also be installed providing the `lab` option:
55
55
 
56
- pip install pypsbuilder[pyqt6,jupyter]
56
+ pip install pypsbuilder[pyqt6,lab]
57
57
 
58
58
  ## I'm using conda or mamba to manage environments
59
59
 
@@ -65,18 +65,23 @@ Using tcinit to setup working directory
65
65
  ---------------------------------------
66
66
 
67
67
  For TC3.5 you can use the ``tcinit`` command to initialize a working
68
- directory. Run it in an empty directory and answer the prompts::
68
+ directory. Run it and answer the prompts::
69
69
 
70
70
  $ tcinit
71
71
 
72
- It downloads THERMOCALC 3.51 for your platform (Linux, Windows or macOS) and
73
- lets you choose one of four bundled datasets/a-x files: ``metapelite``,
74
- ``metabasite``, ``igg25`` (igneous, White et al. 2025 update) or ``igw24``
75
- (igneous, Weller et al. 2024). The chosen dataset, a-x file, starting
76
- guesses and preferences file are written into the project directory,
77
- already tagged with the ``%{PSBGUESS-...}``, ``%{PSBCALC-...}`` and
78
- ``%{PSBBULK-...}`` blocks described above, so the resulting scriptfile works
79
- with ``ptbuilder``/``txbuilder``/``pxbuilder`` out of the box.
72
+ You will be asked for a project directory (defaults to the current
73
+ directory; if it doesn't exist yet but its parent does, it is created for
74
+ you automatically), a project name (max 8 characters), the P-T range to use
75
+ (``[pmin, pmax, tmin, tmax]``, defaulting to ``[1.9, 10.1, 345, 805]`` and
76
+ written into the scriptfile's ``diagramPT``/``calcP``/``calcT`` lines), and
77
+ one of four bundled datasets/a-x files: ``metapelite``, ``metabasite``,
78
+ ``igg25`` (igneous, White et al. 2025 update) or ``igw24`` (igneous, Weller
79
+ et al. 2024). It then downloads THERMOCALC 3.51 for your platform (Linux,
80
+ Windows or macOS). The chosen dataset, a-x file, starting guesses and
81
+ preferences file are written into the project directory, already tagged
82
+ with the ``%{PSBGUESS-...}``, ``%{PSBCALC-...}`` and ``%{PSBBULK-...}``
83
+ blocks described above, so the resulting scriptfile works with
84
+ ``ptbuilder``/``txbuilder``/``pxbuilder`` out of the box.
80
85
 
81
86
 
82
87
  New P-T pseudosection project
@@ -34,12 +34,12 @@ dependencies = [
34
34
  [project.optional-dependencies]
35
35
  pyqt5 = ["pyqt5"]
36
36
  pyqt6 = ["pyqt6"]
37
- jupyter = ["jupyterlab"]
37
+ lab = ["jupyterlab"]
38
38
  tests = ["pytest", "pytest-cov"]
39
39
  docs = ["sphinx", "sphinx_mdinclude", "sphinx_rtd_theme"]
40
40
 
41
41
  [dependency-groups]
42
- dev = ["pypsbuilder[pyqt6,jupyter,tests,docs]", "ruff>=0.6", "pre-commit"]
42
+ dev = ["pypsbuilder[pyqt6,lab,tests,docs]", "ruff>=0.6", "pre-commit"]
43
43
 
44
44
  [project.urls]
45
45
  Homepage = "https://github.com/ondrolexa/pypsbuilder"
@@ -22,7 +22,7 @@ __all__ = (
22
22
  "get_tcapi",
23
23
  )
24
24
 
25
- __version__ = "2.6.5"
25
+ __version__ = "2.6.7"
26
26
  __author__ = "Ondrej Lexa"
27
27
  __copyright__ = "© Ondrej Lexa 2016-2025"
28
28
  __email__ = "lexa.ondrej@gmail.com"
@@ -47,6 +47,9 @@ def tcprojinit():
47
47
  dir_needed = False
48
48
  else:
49
49
  print(f"ERROR: {dest} is not empty...")
50
+ elif not dest.exists() and dest.parent.is_dir():
51
+ dest.mkdir()
52
+ dir_needed = False
50
53
  else:
51
54
  print(f"ERROR: {dest} is not valid directory...")
52
55
 
@@ -71,6 +74,28 @@ def tcprojinit():
71
74
  else:
72
75
  projname_needed = False
73
76
 
77
+ range_needed = True
78
+ while range_needed:
79
+ range_raw = input("Enter [pmin, pmax, tmin, tmax] [1.9, 10.1, 345, 805]: ")
80
+ if range_raw == "":
81
+ pmin, pmax, tmin, tmax = 1.9, 10.1, 345, 805
82
+ else:
83
+ try:
84
+ pmin, pmax, tmin, tmax = (
85
+ float(v) for v in range_raw.strip("[]").split(",")
86
+ )
87
+ except ValueError:
88
+ print("ERROR: enter four comma-separated numbers")
89
+ continue
90
+ if pmax <= pmin:
91
+ print("ERROR: pmax must be greater than pmin")
92
+ elif tmax <= tmin:
93
+ print("ERROR: tmax must be greater than tmin")
94
+ else:
95
+ range_needed = False
96
+
97
+ pstep = (pmax - pmin) / 2
98
+
74
99
  set_needed = True
75
100
  sets = {1: "metapelite", 2: "metabasite", 3: "igg25", 4: "igw24"}
76
101
  while set_needed:
@@ -186,7 +211,15 @@ def tcprojinit():
186
211
  # create project file
187
212
  with open(dest / f"tc-{projname}.txt", "w") as f:
188
213
  content = string.Template(tcprj_tmpl).substitute(
189
- system=system, project=projname, samecoding=samecoding, guesses=guesses
214
+ system=system,
215
+ project=projname,
216
+ samecoding=samecoding,
217
+ guesses=guesses,
218
+ pmin=pmin,
219
+ pmax=pmax,
220
+ tmin=tmin,
221
+ tmax=tmax,
222
+ step=pstep,
190
223
  )[1:]
191
224
  f.write(content)
192
225
 
@@ -803,18 +836,17 @@ tozero no
803
836
  % which conditions
804
837
  % ================
805
838
 
806
- diagramPT 2 10 350 800
839
+ diagramPT $pmin $pmax $tmin $tmax
807
840
 
808
841
  % ================
809
842
  % psbuilder blocks
810
843
  % ================
811
844
 
812
845
  %{PSBCALC-BEGIN}
813
- calcP 11 15 0.08
814
- calcT 370 425
846
+ calcP $pmin $pmax $step
847
+ calcT $tmin $tmax
815
848
  calctatp yes
816
- with bi g mu q ilm hem
817
- zeromodeisopleth q
849
+ with *
818
850
  %{PSBCALC-END}
819
851
 
820
852
  % =====================
@@ -115,7 +115,7 @@ def test_tcprojinit_all_platform_dataset_combinations(
115
115
  ):
116
116
  dest = tmp_path / f"proj_{comp}_{choice}"
117
117
  dest.mkdir()
118
- inputs = iter([str(dest), "testproj", choice])
118
+ inputs = iter([str(dest), "testproj", "", choice])
119
119
 
120
120
  with (
121
121
  mock.patch("builtins.input", lambda *a: next(inputs)),
@@ -139,6 +139,11 @@ def test_tcprojinit_all_platform_dataset_combinations(
139
139
  assert (dest / f"tc-ds{ds}.txt").read_text() == "fake dataset file content\n"
140
140
  assert (dest / f"tc-{system}.txt").read_text() == "fake axfile content\n"
141
141
 
142
+ projfile = (dest / "tc-testproj.txt").read_text()
143
+ assert "diagramPT 1.9 10.1 345 805" in projfile
144
+ assert "calcP 1.9 10.1 4.1" in projfile
145
+ assert "calcT 345 805" in projfile
146
+
142
147
 
143
148
  def test_tcprojinit_samecoding_extracted_from_prefix_matched_file(
144
149
  tmp_path, synthetic_urls
@@ -148,7 +153,7 @@ def test_tcprojinit_samecoding_extracted_from_prefix_matched_file(
148
153
  this session."""
149
154
  dest = tmp_path / "proj"
150
155
  dest.mkdir()
151
- inputs = iter([str(dest), "testproj", "4"]) # igw24
156
+ inputs = iter([str(dest), "testproj", "", "4"]) # igw24
152
157
 
153
158
  with (
154
159
  mock.patch("builtins.input", lambda *a: next(inputs)),
@@ -169,7 +174,7 @@ def test_tcprojinit_no_samecoding_file_falls_back_to_empty(tmp_path):
169
174
  current code falls back to an empty string rather than raising."""
170
175
  dest = tmp_path / "proj"
171
176
  dest.mkdir()
172
- inputs = iter([str(dest), "testproj", "1"]) # metapelite
177
+ inputs = iter([str(dest), "testproj", "", "1"]) # metapelite
173
178
 
174
179
  cache = {
175
180
  tcinit.exe_urls["linux"]: _exe_zip("tc351-linux-bundle", "tc351lin"),
@@ -192,6 +197,45 @@ def test_tcprojinit_no_samecoding_file_falls_back_to_empty(tmp_path):
192
197
  assert projfile.exists()
193
198
 
194
199
 
200
+ def test_tcprojinit_creates_missing_directory_with_existing_parent(
201
+ tmp_path, synthetic_urls
202
+ ):
203
+ dest = tmp_path / "newdir"
204
+ assert not dest.exists()
205
+ inputs = iter([str(dest), "testproj", "", "1"])
206
+
207
+ with (
208
+ mock.patch("builtins.input", lambda *a: next(inputs)),
209
+ mock.patch("pypsbuilder.tcinit.platform.system", return_value="Linux"),
210
+ mock.patch("pypsbuilder.tcinit.urlopen", _fake_urlopen(synthetic_urls)),
211
+ ):
212
+ tcinit.tcprojinit()
213
+
214
+ assert dest.is_dir()
215
+ assert (dest / "tc-prefs.txt").exists()
216
+ assert (dest / "tc-testproj.txt").exists()
217
+
218
+
219
+ def test_tcprojinit_rejects_missing_parent_directory(tmp_path, synthetic_urls, capsys):
220
+ dest = tmp_path / "missing" / "newdir"
221
+ assert not dest.parent.exists()
222
+
223
+ good_dest = tmp_path / "empty_proj"
224
+ good_dest.mkdir()
225
+
226
+ inputs = iter([str(dest), str(good_dest), "testproj", "", "1"])
227
+
228
+ with (
229
+ mock.patch("builtins.input", lambda *a: next(inputs)),
230
+ mock.patch("pypsbuilder.tcinit.platform.system", return_value="Linux"),
231
+ mock.patch("pypsbuilder.tcinit.urlopen", _fake_urlopen(synthetic_urls)),
232
+ ):
233
+ tcinit.tcprojinit()
234
+
235
+ assert "is not valid directory" in capsys.readouterr().out
236
+ assert (good_dest / "tc-prefs.txt").exists()
237
+
238
+
195
239
  def test_tcprojinit_rejects_nonempty_directory(tmp_path, synthetic_urls, capsys):
196
240
  dest = tmp_path / "proj"
197
241
  dest.mkdir()
@@ -200,7 +244,7 @@ def test_tcprojinit_rejects_nonempty_directory(tmp_path, synthetic_urls, capsys)
200
244
  good_dest = tmp_path / "empty_proj"
201
245
  good_dest.mkdir()
202
246
 
203
- inputs = iter([str(dest), str(good_dest), "testproj", "1"])
247
+ inputs = iter([str(dest), str(good_dest), "testproj", "", "1"])
204
248
 
205
249
  with (
206
250
  mock.patch("builtins.input", lambda *a: next(inputs)),
@@ -216,7 +260,7 @@ def test_tcprojinit_rejects_nonempty_directory(tmp_path, synthetic_urls, capsys)
216
260
  def test_tcprojinit_default_project_name(tmp_path, synthetic_urls):
217
261
  dest = tmp_path / "proj"
218
262
  dest.mkdir()
219
- inputs = iter([str(dest), "", "1"]) # empty name -> default "project"
263
+ inputs = iter([str(dest), "", "", "1"]) # empty name -> default "project"
220
264
 
221
265
  with (
222
266
  mock.patch("builtins.input", lambda *a: next(inputs)),
@@ -229,13 +273,46 @@ def test_tcprojinit_default_project_name(tmp_path, synthetic_urls):
229
273
  assert "scriptfile project" in (dest / "tc-prefs.txt").read_text()
230
274
 
231
275
 
276
+ def test_tcprojinit_range_validation_and_retry(tmp_path, synthetic_urls, capsys):
277
+ dest = tmp_path / "proj"
278
+ dest.mkdir()
279
+ inputs = iter(
280
+ [
281
+ str(dest),
282
+ "testproj",
283
+ "not, a, number, here", # unparseable -> retry
284
+ "10, 2, 345, 805", # pmax <= pmin -> retry
285
+ "1.9, 10.1, 805, 345", # tmax <= tmin -> retry
286
+ "2, 12, 400, 700", # valid
287
+ "1",
288
+ ]
289
+ )
290
+
291
+ with (
292
+ mock.patch("builtins.input", lambda *a: next(inputs)),
293
+ mock.patch("pypsbuilder.tcinit.platform.system", return_value="Linux"),
294
+ mock.patch("pypsbuilder.tcinit.urlopen", _fake_urlopen(synthetic_urls)),
295
+ ):
296
+ tcinit.tcprojinit()
297
+
298
+ out = capsys.readouterr().out
299
+ assert "ERROR: enter four comma-separated numbers" in out
300
+ assert "ERROR: pmax must be greater than pmin" in out
301
+ assert "ERROR: tmax must be greater than tmin" in out
302
+
303
+ projfile = (dest / "tc-testproj.txt").read_text()
304
+ assert "diagramPT 2.0 12.0 400.0 700.0" in projfile
305
+ assert "calcP 2.0 12.0 5.0" in projfile
306
+ assert "calcT 400.0 700.0" in projfile
307
+
308
+
232
309
  def test_tcprojinit_project_name_sanitization(tmp_path, synthetic_urls):
233
310
  dest = tmp_path / "proj"
234
311
  dest.mkdir()
235
312
  # spaces replaced, non-ascii stripped, then re-entered because
236
313
  # "a b" -> "a_b" is valid on first pass (no retry needed here);
237
314
  # test a name that's too long first to exercise the retry loop
238
- inputs = iter([str(dest), "toolongname123", "short nm", "1"])
315
+ inputs = iter([str(dest), "toolongname123", "short nm", "", "1"])
239
316
 
240
317
  with (
241
318
  mock.patch("builtins.input", lambda *a: next(inputs)),