gitronics 0.6.0__tar.gz → 0.6.2__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. {gitronics-0.6.0 → gitronics-0.6.2}/.github/workflows/ci.yml +19 -1
  2. {gitronics-0.6.0 → gitronics-0.6.2}/Cargo.lock +1 -1
  3. {gitronics-0.6.0 → gitronics-0.6.2}/Cargo.toml +1 -1
  4. {gitronics-0.6.0 → gitronics-0.6.2}/PKG-INFO +1 -1
  5. {gitronics-0.6.0 → gitronics-0.6.2}/python/tests/test_cli_works.py +6 -2
  6. {gitronics-0.6.0 → gitronics-0.6.2}/src/project_manager/load_project_files.rs +5 -2
  7. {gitronics-0.6.0 → gitronics-0.6.2}/src/utils.rs +76 -0
  8. {gitronics-0.6.0 → gitronics-0.6.2}/.github/workflows/docs.yml +0 -0
  9. {gitronics-0.6.0 → gitronics-0.6.2}/.github/workflows/release.yml +0 -0
  10. {gitronics-0.6.0 → gitronics-0.6.2}/.gitignore +0 -0
  11. {gitronics-0.6.0 → gitronics-0.6.2}/.vscode/settings.json +0 -0
  12. {gitronics-0.6.0 → gitronics-0.6.2}/LICENSE +0 -0
  13. {gitronics-0.6.0 → gitronics-0.6.2}/README.md +0 -0
  14. {gitronics-0.6.0 → gitronics-0.6.2}/docs/assets/logo.png +0 -0
  15. {gitronics-0.6.0 → gitronics-0.6.2}/docs/best_practices.md +0 -0
  16. {gitronics-0.6.0 → gitronics-0.6.2}/docs/examples.md +0 -0
  17. {gitronics-0.6.0 → gitronics-0.6.2}/docs/getting_started/assessments.md +0 -0
  18. {gitronics-0.6.0 → gitronics-0.6.2}/docs/getting_started/building_a_model.md +0 -0
  19. {gitronics-0.6.0 → gitronics-0.6.2}/docs/getting_started/card_id_numbers.md +0 -0
  20. {gitronics-0.6.0 → gitronics-0.6.2}/docs/getting_started/concepts.md +0 -0
  21. {gitronics-0.6.0 → gitronics-0.6.2}/docs/getting_started/configuration_file.md +0 -0
  22. {gitronics-0.6.0 → gitronics-0.6.2}/docs/getting_started/file_types.md +0 -0
  23. {gitronics-0.6.0 → gitronics-0.6.2}/docs/getting_started/migrating.md +0 -0
  24. {gitronics-0.6.0 → gitronics-0.6.2}/docs/getting_started/new_project.md +0 -0
  25. {gitronics-0.6.0 → gitronics-0.6.2}/docs/getting_started/project_structure.md +0 -0
  26. {gitronics-0.6.0 → gitronics-0.6.2}/docs/getting_started.md +0 -0
  27. {gitronics-0.6.0 → gitronics-0.6.2}/docs/hooks/version_from_git.py +0 -0
  28. {gitronics-0.6.0 → gitronics-0.6.2}/docs/index.md +0 -0
  29. {gitronics-0.6.0 → gitronics-0.6.2}/docs/installation.md +0 -0
  30. {gitronics-0.6.0 → gitronics-0.6.2}/docs/requirements.txt +0 -0
  31. {gitronics-0.6.0 → gitronics-0.6.2}/docs/usage/build.md +0 -0
  32. {gitronics-0.6.0 → gitronics-0.6.2}/docs/usage/migrate.md +0 -0
  33. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/assessment_specific/filler_model_3.mcnp +0 -0
  34. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/assessment_specific/filler_model_3.metadata +0 -0
  35. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/assessment_specific/small_override.yaml +0 -0
  36. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/configurations/valid_configuration.yaml +0 -0
  37. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/output/.gitignore +0 -0
  38. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/reference_model/data_cards/fine_mesh.tally +0 -0
  39. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/reference_model/data_cards/materials.mat +0 -0
  40. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/reference_model/data_cards/my_transform.transform +0 -0
  41. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/reference_model/data_cards/volumetric_source.source +0 -0
  42. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/reference_model/envelope_structure.mcnp +0 -0
  43. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/reference_model/envelope_structure.metadata +0 -0
  44. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/reference_model/filler_models/filler_model_1.mcnp +0 -0
  45. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/reference_model/filler_models/filler_model_1.metadata +0 -0
  46. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/reference_model/filler_models/filler_model_2.mcnp +0 -0
  47. {gitronics-0.6.0 → gitronics-0.6.2}/example_project/reference_model/filler_models/filler_model_2.metadata +0 -0
  48. {gitronics-0.6.0 → gitronics-0.6.2}/mkdocs.yml +0 -0
  49. {gitronics-0.6.0 → gitronics-0.6.2}/pyproject.toml +0 -0
  50. {gitronics-0.6.0 → gitronics-0.6.2}/python/gitronics/__init__.py +0 -0
  51. {gitronics-0.6.0 → gitronics-0.6.2}/python/gitronics/__main__.py +0 -0
  52. {gitronics-0.6.0 → gitronics-0.6.2}/python/gitronics/gitronics.pyi +0 -0
  53. {gitronics-0.6.0 → gitronics-0.6.2}/python/tests/test_build_model.py +0 -0
  54. {gitronics-0.6.0 → gitronics-0.6.2}/resources/simple_model.mcnp +0 -0
  55. {gitronics-0.6.0 → gitronics-0.6.2}/src/build_model.rs +0 -0
  56. {gitronics-0.6.0 → gitronics-0.6.2}/src/build_report.rs +0 -0
  57. {gitronics-0.6.0 → gitronics-0.6.2}/src/cli.rs +0 -0
  58. {gitronics-0.6.0 → gitronics-0.6.2}/src/lib.rs +0 -0
  59. {gitronics-0.6.0 → gitronics-0.6.2}/src/main.rs +0 -0
  60. {gitronics-0.6.0 → gitronics-0.6.2}/src/migrate_model.rs +0 -0
  61. {gitronics-0.6.0 → gitronics-0.6.2}/src/model_config.rs +0 -0
  62. {gitronics-0.6.0 → gitronics-0.6.2}/src/project_manager/load_metadata.rs +0 -0
  63. {gitronics-0.6.0 → gitronics-0.6.2}/src/project_manager/load_model_config.rs +0 -0
  64. {gitronics-0.6.0 → gitronics-0.6.2}/src/project_manager.rs +0 -0
  65. {gitronics-0.6.0 → gitronics-0.6.2}/src/python.rs +0 -0
  66. {gitronics-0.6.0 → gitronics-0.6.2}/src/report.css +0 -0
  67. {gitronics-0.6.0 → gitronics-0.6.2}/src/types.rs +0 -0
  68. {gitronics-0.6.0 → gitronics-0.6.2}/tests/test_example_project.rs +0 -0
@@ -22,7 +22,7 @@ jobs:
22
22
  - name: Install Rust stable
23
23
  uses: dtolnay/rust-toolchain@stable
24
24
  with:
25
- components: clippy
25
+ components: clippy, rustfmt
26
26
 
27
27
  - name: Set up Python (Required for PyO3 test linking)
28
28
  id: setup-python
@@ -49,12 +49,30 @@ jobs:
49
49
  target
50
50
  key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
51
51
 
52
+ - name: rustfmt
53
+ run: cargo fmt --all --check
54
+
52
55
  - name: Test
53
56
  run: cargo test --workspace --features pyo3/auto-initialize
54
57
 
55
58
  - name: Clippy
56
59
  run: cargo clippy --workspace -- -D warnings
57
60
 
61
+ ruff:
62
+ name: Python lint (ruff)
63
+ runs-on: ubuntu-latest
64
+ steps:
65
+ - uses: actions/checkout@v4
66
+
67
+ - uses: astral-sh/setup-uv@v5
68
+
69
+ # Pinned so a new ruff release can't fail CI unexpectedly; bump deliberately.
70
+ - name: ruff lint
71
+ run: uvx ruff@0.15.21 check python
72
+
73
+ - name: ruff format
74
+ run: uvx ruff@0.15.21 format --check python
75
+
58
76
  python:
59
77
  name: Python tests (${{ matrix.os }})
60
78
  runs-on: ${{ matrix.os }}
@@ -390,7 +390,7 @@ dependencies = [
390
390
 
391
391
  [[package]]
392
392
  name = "gitronics"
393
- version = "0.6.0"
393
+ version = "0.6.2"
394
394
  dependencies = [
395
395
  "chrono",
396
396
  "clap",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "gitronics"
3
- version = "0.6.0"
3
+ version = "0.6.2"
4
4
  edition = "2024"
5
5
  description = "Build MCNP neutronics models from modular components"
6
6
  license = "EUPL-1.2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitronics
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -6,8 +6,12 @@ def test_cli_works():
6
6
  # Run in a subprocess so clap's process::exit(0) on --help doesn't kill pytest.
7
7
  # Actual behaviour is covered by the Rust unit tests.
8
8
  result = subprocess.run(
9
- [sys.executable, "-c", "from gitronics import _cli_main; _cli_main()", "--help"],
9
+ [
10
+ sys.executable,
11
+ "-c",
12
+ "from gitronics import _cli_main; _cli_main()",
13
+ "--help",
14
+ ],
10
15
  capture_output=True,
11
16
  )
12
17
  assert result.returncode == 0
13
-
@@ -10,7 +10,7 @@ use migjorn::Model;
10
10
  use rayon::prelude::*;
11
11
 
12
12
  use crate::types::{FileName, FillerName};
13
- use crate::utils::{GitronicsError, parse_model_file, read_data_cards_text};
13
+ use crate::utils::{GitronicsError, parse_model_file, read_data_cards_text, sort_data_card_chunks};
14
14
 
15
15
  use super::ProjectManager;
16
16
 
@@ -91,7 +91,9 @@ impl ProjectManager {
91
91
  }
92
92
 
93
93
  /// Concatenates the data-card text of every file in `names`, one blank-line
94
- /// separated block per file, in configuration order.
94
+ /// separated block per file. Blocks are ordered by the id of each file's
95
+ /// first data card (deterministic output, independent of configuration
96
+ /// order); cards inside a file keep their original order.
95
97
  fn load_data_cards_text(&self, names: &[FileName]) -> Result<String, GitronicsError> {
96
98
  let mut chunks = Vec::new();
97
99
  for name in names {
@@ -99,6 +101,7 @@ impl ProjectManager {
99
101
  info!("Loading: {}", path.display());
100
102
  chunks.push(read_data_cards_text(path, name)?);
101
103
  }
104
+ sort_data_card_chunks(&mut chunks);
102
105
  Ok(chunks.join("\n"))
103
106
  }
104
107
  }
@@ -158,6 +158,53 @@ pub fn read_data_cards_text(path: &Path, file_name: &FileName) -> Result<String,
158
158
  Ok(kept.join("\n"))
159
159
  }
160
160
 
161
+ /// Sort key mirroring a data card's id: cards whose mnemonic ends in a number
162
+ /// (`M1`, `F4`, `TR1`) order by that number and come first; mnemonics without a
163
+ /// trailing number (`SDEF`, `MODE`) fall back to alphabetical order and come
164
+ /// last — matching the old `DataCardId::{Int, String}` ordering.
165
+ #[derive(PartialEq, Eq, PartialOrd, Ord)]
166
+ enum DataCardKey {
167
+ Int(u32),
168
+ Str(String),
169
+ }
170
+
171
+ /// The sort key of a data-card file block, derived from the id of its first
172
+ /// data card. Full-line comments (and blank lines) are skipped so a comment
173
+ /// header does not decide the order.
174
+ fn first_data_card_key(chunk: &str) -> DataCardKey {
175
+ for line in chunk.lines() {
176
+ if is_mcnp_comment(line) {
177
+ continue;
178
+ }
179
+ // First whitespace token is the mnemonic, minus any leading `*`.
180
+ let mnemonic = line
181
+ .split_whitespace()
182
+ .next()
183
+ .unwrap_or("")
184
+ .trim_start_matches('*');
185
+ // Split the trailing id number off the mnemonic letters (`TR1` -> 1,
186
+ // `F4:N` -> 4).
187
+ let digits: String = mnemonic
188
+ .chars()
189
+ .skip_while(|c| !c.is_ascii_digit())
190
+ .take_while(|c| c.is_ascii_digit())
191
+ .collect();
192
+ return match digits.parse::<u32>() {
193
+ Ok(id) => DataCardKey::Int(id),
194
+ Err(_) => DataCardKey::Str(mnemonic.to_ascii_uppercase()),
195
+ };
196
+ }
197
+ DataCardKey::Str(String::new())
198
+ }
199
+
200
+ /// Order data-card file blocks by the id of each block's first data card,
201
+ /// keeping the cards inside a block in their original order (a stable sort over
202
+ /// whole blocks). Restores the deterministic output the pre-migjorn-0.2 build
203
+ /// produced via `order_data_cards_groups_by_id`.
204
+ pub fn sort_data_card_chunks(chunks: &mut [String]) {
205
+ chunks.sort_by_key(|chunk| first_data_card_key(chunk));
206
+ }
207
+
161
208
  /// True if `line` is an MCNP full-line comment (first non-blank character is
162
209
  /// `c`/`C`, followed by whitespace or end of line) or blank.
163
210
  fn is_mcnp_comment(line: &str) -> bool {
@@ -282,6 +329,35 @@ mod tests {
282
329
  assert!(err.to_string().contains("Duplicate file name `file1`"));
283
330
  }
284
331
 
332
+ #[test]
333
+ fn test_sort_data_card_chunks_by_first_card_id() {
334
+ // Blocks arrive in arbitrary configuration order.
335
+ let mut chunks = vec![
336
+ "m10 1001 1\nm2 8016 1".to_string(), // first card M10 -> 10
337
+ "C header\nm1 6000 1".to_string(), // comment skipped, M1 -> 1
338
+ "sdef pos=0 0 0".to_string(), // no id -> sorts last
339
+ "m3 26000 1".to_string(), // M3 -> 3
340
+ ];
341
+ sort_data_card_chunks(&mut chunks);
342
+ assert_eq!(
343
+ chunks,
344
+ vec![
345
+ "C header\nm1 6000 1".to_string(),
346
+ "m3 26000 1".to_string(),
347
+ "m10 1001 1\nm2 8016 1".to_string(),
348
+ "sdef pos=0 0 0".to_string(),
349
+ ]
350
+ );
351
+ }
352
+
353
+ #[test]
354
+ fn test_sort_data_card_chunks_is_stable_within_block() {
355
+ // Cards inside a block are never reordered, even if not id-sorted.
356
+ let mut chunks = vec!["m5 1001 1\nm2 8016 1".to_string()];
357
+ sort_data_card_chunks(&mut chunks);
358
+ assert_eq!(chunks, vec!["m5 1001 1\nm2 8016 1".to_string()]);
359
+ }
360
+
285
361
  #[test]
286
362
  fn test_io_path_normalizes_parent_segments_when_not_canonicalizable() {
287
363
  let err = GitronicsError::io_path(
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes