jcl-parser 2.0.3__tar.gz → 2.2.0__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 (87) hide show
  1. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/PKG-INFO +69 -11
  2. jcl_parser-2.2.0/README.md +115 -0
  3. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jcl_parser.egg-info/PKG-INFO +69 -11
  4. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jcl_parser.egg-info/SOURCES.txt +5 -0
  5. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jcl_parser.egg-info/entry_points.txt +1 -0
  6. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jcl_parser.egg-info/requires.txt +3 -1
  7. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/__init__.py +45 -5
  8. jcl_parser-2.2.0/jclparser/_include_origins.py +129 -0
  9. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/_job_builder.py +238 -55
  10. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/_model_factories.py +28 -1
  11. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/_post_parse.py +163 -89
  12. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/_source_scanner.py +109 -38
  13. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/_statement_parser.py +72 -16
  14. jcl_parser-2.2.0/jclparser/checks.py +382 -0
  15. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/cli.py +486 -200
  16. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/condition_parser.py +73 -76
  17. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/continuation_handler.py +62 -23
  18. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/dataset_lifecycle.py +68 -31
  19. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/errors.py +31 -7
  20. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/explosion.py +493 -248
  21. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/include_loader.py +173 -92
  22. jcl_parser-2.2.0/jclparser/lineage.py +498 -0
  23. jcl_parser-2.2.0/jclparser/lint/__init__.py +47 -0
  24. jcl_parser-2.2.0/jclparser/lint/config.py +189 -0
  25. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/lint/rules.py +119 -9
  26. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/lint/runner.py +62 -36
  27. jcl_parser-2.2.0/jclparser/lint/suppress.py +81 -0
  28. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/metrics.py +4 -1
  29. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/model.py +79 -4
  30. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/parameter_ast.py +4 -2
  31. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/parameter_validators.py +58 -50
  32. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/parser.py +88 -27
  33. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/plugins/base.py +7 -0
  34. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/proc_loader.py +18 -6
  35. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/profiling.py +5 -1
  36. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/_control.py +6 -3
  37. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/_invoked.py +2 -1
  38. jcl_parser-2.2.0/jclparser/program_profiles/adrdssu.py +116 -0
  39. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/base.py +8 -3
  40. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/binder.py +24 -5
  41. jcl_parser-2.2.0/jclparser/program_profiles/ftp.py +187 -0
  42. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/icetool.py +2 -1
  43. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/iefbr14.py +9 -2
  44. jcl_parser-2.2.0/jclparser/program_profiles/iehlist.py +87 -0
  45. jcl_parser-2.2.0/jclparser/program_profiles/iehprogm.py +94 -0
  46. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/sort.py +65 -9
  47. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/rc_analysis.py +60 -7
  48. jcl_parser-2.2.0/jclparser/serialization.py +330 -0
  49. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/sql_detector.py +56 -17
  50. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/utils.py +88 -3
  51. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/validators.py +263 -76
  52. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/pyproject.toml +9 -6
  53. jcl_parser-2.0.3/README.md +0 -58
  54. jcl_parser-2.0.3/jclparser/lint/__init__.py +0 -28
  55. jcl_parser-2.0.3/jclparser/lint/config.py +0 -39
  56. jcl_parser-2.0.3/jclparser/program_profiles/adrdssu.py +0 -72
  57. jcl_parser-2.0.3/jclparser/program_profiles/ftp.py +0 -59
  58. jcl_parser-2.0.3/jclparser/program_profiles/iehlist.py +0 -54
  59. jcl_parser-2.0.3/jclparser/program_profiles/iehprogm.py +0 -108
  60. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/LICENSE +0 -0
  61. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jcl_parser.egg-info/dependency_links.txt +0 -0
  62. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jcl_parser.egg-info/top_level.txt +0 -0
  63. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/__main__.py +0 -0
  64. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/builtin_procs.py +0 -0
  65. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/jcl_file_type.py +0 -0
  66. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/jes_statements.py +0 -0
  67. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/lint/base.py +0 -0
  68. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/plugins/__init__.py +0 -0
  69. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/__init__.py +0 -0
  70. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/bpxbatch.py +0 -0
  71. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/compilers.py +0 -0
  72. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/db2_batch.py +0 -0
  73. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/dfsrrc00.py +0 -0
  74. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/dsnutilb.py +0 -0
  75. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/idcams.py +0 -0
  76. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/iebcompr.py +0 -0
  77. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/iebdg.py +0 -0
  78. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/iebgener.py +0 -0
  79. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/iebptpch.py +0 -0
  80. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/iebupdte.py +0 -0
  81. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/iehinitt.py +0 -0
  82. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/ikjeft01.py +0 -0
  83. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/irxjcl.py +0 -0
  84. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/program_profiles/registry.py +0 -0
  85. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/py.typed +0 -0
  86. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/jclparser/system_symbols.py +0 -0
  87. {jcl_parser-2.0.3 → jcl_parser-2.2.0}/setup.cfg +0 -0
@@ -1,17 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jcl-parser
3
- Version: 2.0.3
3
+ Version: 2.2.0
4
4
  Summary: Parse IBM JCL (Job Control Language) scripts into Python objects
5
5
  Author-email: Samuel Dion <sam94dion@gmail.com>
6
- License: AGPL-3.0-or-later
6
+ License-Expression: AGPL-3.0-or-later
7
7
  Project-URL: Homepage, https://github.com/landparty/jcl-parser
8
8
  Project-URL: Repository, https://github.com/landparty/jcl-parser
9
- Project-URL: Documentation, https://github.com/landparty/jcl-parser/tree/main/docs
9
+ Project-URL: Documentation, https://github.com/landparty/jcl-parser/blob/main/.okf/index.md
10
10
  Project-URL: Bug Reports, https://github.com/landparty/jcl-parser/issues
11
11
  Keywords: jcl,mainframe,parser,ibm,job-control-language
12
12
  Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Intended Audience :: Developers
14
- Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
15
14
  Classifier: Operating System :: OS Independent
16
15
  Classifier: Programming Language :: Python :: 3
17
16
  Classifier: Programming Language :: Python :: 3.10
@@ -28,12 +27,14 @@ Requires-Dist: pytest>=7.0; extra == "dev"
28
27
  Requires-Dist: coverage[toml]>=7.0; extra == "dev"
29
28
  Requires-Dist: pytest-cov>=4.0; extra == "dev"
30
29
  Requires-Dist: pytest-benchmark>=4.0; extra == "dev"
31
- Requires-Dist: ruff>=0.1; extra == "dev"
30
+ Requires-Dist: ruff==0.17.3; extra == "dev"
32
31
  Requires-Dist: mypy>=1.0; extra == "dev"
32
+ Requires-Dist: jsonschema>=4.18; extra == "dev"
33
33
  Requires-Dist: tomli>=1.1.0; python_version < "3.11" and extra == "dev"
34
34
  Provides-Extra: test
35
35
  Requires-Dist: pytest>=7.0; extra == "test"
36
36
  Requires-Dist: pytest-cov>=4.0; extra == "test"
37
+ Requires-Dist: jsonschema>=4.18; extra == "test"
37
38
  Requires-Dist: tomli>=1.1.0; python_version < "3.11" and extra == "test"
38
39
  Provides-Extra: profiling
39
40
  Requires-Dist: psutil>=5.9; extra == "profiling"
@@ -50,7 +51,8 @@ Python library for parsing IBM z/OS JCL (Job Control Language) into structured `
50
51
  ## Install
51
52
 
52
53
  ```bash
53
- pip install -e ".[dev]"
54
+ pip install jcl-parser # from PyPI
55
+ pip install -e ".[dev]" # from a checkout, with the dev tools
54
56
  ```
55
57
 
56
58
  Requires Python 3.10+. No runtime dependencies.
@@ -58,11 +60,20 @@ Requires Python 3.10+. No runtime dependencies.
58
60
  ## Quick Start
59
61
 
60
62
  ```python
63
+ from pathlib import Path
64
+
61
65
  import jclparser
62
66
 
63
- job = jclparser.parse_jcl(source)
67
+ job = jclparser.parse_jcl(Path("PAYROLL.jcl")) # a str is always JCL text, never a path
64
68
  print(job.name)
65
- print(job.steps[0].program)
69
+ for step in jclparser.iter_execution_steps(job.steps): # also walks IF/THEN/ELSE blocks
70
+ print(step.name, step.program or step.procedure)
71
+
72
+ exploded = jclparser.explode_job(job) # PROC expansion is a separate call
73
+ errors = jclparser.validate_jcl(
74
+ Path("PAYROLL.jcl"), level="structure"
75
+ ) # list[ParseError]
76
+ print(job.to_json(indent=2)) # schema: jclparser.JOB_JSON_SCHEMA
66
77
  ```
67
78
 
68
79
  CLI:
@@ -71,7 +82,52 @@ CLI:
71
82
  jcl-parser FILE steps # list all steps
72
83
  jcl-parser FILE --validate # validate JCL
73
84
  jcl-parser FILE lint # run lint rules
74
- jcl-parser FILE --json # full parse result as JSON
85
+ jcl-parser FILE --json # full parse result as JSON (job.to_dict())
86
+ jcl-parser --json-schema # JSON Schema of that document
87
+ jcl-parser DIR # cross-job dataset lineage of a directory of jobs
88
+ jcl-parser DIR lineage mermaid # ... as a Mermaid graph (or `dot`)
89
+ jcl-parser DIR lint --sarif # lint every job of a directory, as SARIF
90
+ jcl-lint jobs/ --fail-on warning --format sarif -o jcl.sarif # CI / pre-commit
91
+ ```
92
+
93
+ ### Lint in CI
94
+
95
+ Configure the rules in `pyproject.toml`, and switch one off for one statement
96
+ with a comment:
97
+
98
+ ```toml
99
+ [tool.jcl-parser.lint]
100
+ ignore = ["JCL-LINT-002"]
101
+ fail-on = "warning"
102
+ [tool.jcl-parser.lint.severity]
103
+ "JCL-LINT-004" = "warning"
104
+ ```
105
+
106
+ ```jcl
107
+ //* jcl-lint: disable=JCL-LINT-001
108
+ //MASTER DD DSN=PROD.MASTER.FILE,DISP=SHR
109
+ ```
110
+
111
+ pre-commit (`.pre-commit-config.yaml`):
112
+
113
+ ```yaml
114
+ - repo: https://github.com/landparty/jcl-parser
115
+ rev: v2.2.0 # first release with the hook
116
+ hooks:
117
+ - id: jcl-lint
118
+ ```
119
+
120
+ GitHub Actions (findings appear in code scanning):
121
+
122
+ ```yaml
123
+ permissions:
124
+ security-events: write
125
+ steps:
126
+ - uses: actions/checkout@v4
127
+ - uses: landparty/jcl-parser@v2.2.0 # first release with the action
128
+ with:
129
+ paths: jcl
130
+ glob: "**/*.jcl"
75
131
  ```
76
132
 
77
133
  ## Commands
@@ -83,7 +139,7 @@ pytest -m unit
83
139
 
84
140
  # Lint / format / type-check
85
141
  ruff check . && ruff format .
86
- mypy jclparser/
142
+ python scripts/mypy_ratchet.py # mypy strict against the known-error baseline, as CI runs it
87
143
 
88
144
  # Vendor sync (after changes to jclparser/) — rm first: plain `cp -r` nests
89
145
  # into dest/jclparser/jclparser/ and leaves stale files when dest exists
@@ -92,7 +148,9 @@ rm -rf ../backend/_vendor/jcl-parser/jclparser && cp -r jclparser/ ../backend/_v
92
148
 
93
149
  ## Documentation
94
150
 
95
- Full reference: [`.okf/index.md`](.okf/index.md)
151
+ Full reference: [`.okf/index.md`](.okf/index.md). Contributors: start with [`CLAUDE.md`](CLAUDE.md)
152
+ (commands, architecture, invariants) and [`.okf/development/invariants.md`](.okf/development/invariants.md).
153
+ Changes are listed in [`CHANGELOG.md`](CHANGELOG.md) (releases) and [`.okf/log.md`](.okf/log.md).
96
154
 
97
155
  ## License
98
156
 
@@ -0,0 +1,115 @@
1
+ # jcl-parser
2
+
3
+ Python library for parsing IBM z/OS JCL (Job Control Language) into structured `Job`, `Step`, and `DD` objects. Supports PROC expansion, validation, lint rules, SQL detection, and dataset lifecycle analysis.
4
+
5
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
6
+ [![Coverage](https://img.shields.io/badge/coverage-90%25%2B%20enforced-green.svg)](#commands)
7
+ [![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](LICENSE)
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install jcl-parser # from PyPI
13
+ pip install -e ".[dev]" # from a checkout, with the dev tools
14
+ ```
15
+
16
+ Requires Python 3.10+. No runtime dependencies.
17
+
18
+ ## Quick Start
19
+
20
+ ```python
21
+ from pathlib import Path
22
+
23
+ import jclparser
24
+
25
+ job = jclparser.parse_jcl(Path("PAYROLL.jcl")) # a str is always JCL text, never a path
26
+ print(job.name)
27
+ for step in jclparser.iter_execution_steps(job.steps): # also walks IF/THEN/ELSE blocks
28
+ print(step.name, step.program or step.procedure)
29
+
30
+ exploded = jclparser.explode_job(job) # PROC expansion is a separate call
31
+ errors = jclparser.validate_jcl(
32
+ Path("PAYROLL.jcl"), level="structure"
33
+ ) # list[ParseError]
34
+ print(job.to_json(indent=2)) # schema: jclparser.JOB_JSON_SCHEMA
35
+ ```
36
+
37
+ CLI:
38
+
39
+ ```bash
40
+ jcl-parser FILE steps # list all steps
41
+ jcl-parser FILE --validate # validate JCL
42
+ jcl-parser FILE lint # run lint rules
43
+ jcl-parser FILE --json # full parse result as JSON (job.to_dict())
44
+ jcl-parser --json-schema # JSON Schema of that document
45
+ jcl-parser DIR # cross-job dataset lineage of a directory of jobs
46
+ jcl-parser DIR lineage mermaid # ... as a Mermaid graph (or `dot`)
47
+ jcl-parser DIR lint --sarif # lint every job of a directory, as SARIF
48
+ jcl-lint jobs/ --fail-on warning --format sarif -o jcl.sarif # CI / pre-commit
49
+ ```
50
+
51
+ ### Lint in CI
52
+
53
+ Configure the rules in `pyproject.toml`, and switch one off for one statement
54
+ with a comment:
55
+
56
+ ```toml
57
+ [tool.jcl-parser.lint]
58
+ ignore = ["JCL-LINT-002"]
59
+ fail-on = "warning"
60
+ [tool.jcl-parser.lint.severity]
61
+ "JCL-LINT-004" = "warning"
62
+ ```
63
+
64
+ ```jcl
65
+ //* jcl-lint: disable=JCL-LINT-001
66
+ //MASTER DD DSN=PROD.MASTER.FILE,DISP=SHR
67
+ ```
68
+
69
+ pre-commit (`.pre-commit-config.yaml`):
70
+
71
+ ```yaml
72
+ - repo: https://github.com/landparty/jcl-parser
73
+ rev: v2.2.0 # first release with the hook
74
+ hooks:
75
+ - id: jcl-lint
76
+ ```
77
+
78
+ GitHub Actions (findings appear in code scanning):
79
+
80
+ ```yaml
81
+ permissions:
82
+ security-events: write
83
+ steps:
84
+ - uses: actions/checkout@v4
85
+ - uses: landparty/jcl-parser@v2.2.0 # first release with the action
86
+ with:
87
+ paths: jcl
88
+ glob: "**/*.jcl"
89
+ ```
90
+
91
+ ## Commands
92
+
93
+ ```bash
94
+ # Tests
95
+ pytest
96
+ pytest -m unit
97
+
98
+ # Lint / format / type-check
99
+ ruff check . && ruff format .
100
+ python scripts/mypy_ratchet.py # mypy strict against the known-error baseline, as CI runs it
101
+
102
+ # Vendor sync (after changes to jclparser/) — rm first: plain `cp -r` nests
103
+ # into dest/jclparser/jclparser/ and leaves stale files when dest exists
104
+ rm -rf ../backend/_vendor/jcl-parser/jclparser && cp -r jclparser/ ../backend/_vendor/jcl-parser/jclparser
105
+ ```
106
+
107
+ ## Documentation
108
+
109
+ Full reference: [`.okf/index.md`](.okf/index.md). Contributors: start with [`CLAUDE.md`](CLAUDE.md)
110
+ (commands, architecture, invariants) and [`.okf/development/invariants.md`](.okf/development/invariants.md).
111
+ Changes are listed in [`CHANGELOG.md`](CHANGELOG.md) (releases) and [`.okf/log.md`](.okf/log.md).
112
+
113
+ ## License
114
+
115
+ GNU Affero General Public License v3.0 or later — Copyright © 2026 Samuel Dion. See [LICENSE](LICENSE).
@@ -1,17 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jcl-parser
3
- Version: 2.0.3
3
+ Version: 2.2.0
4
4
  Summary: Parse IBM JCL (Job Control Language) scripts into Python objects
5
5
  Author-email: Samuel Dion <sam94dion@gmail.com>
6
- License: AGPL-3.0-or-later
6
+ License-Expression: AGPL-3.0-or-later
7
7
  Project-URL: Homepage, https://github.com/landparty/jcl-parser
8
8
  Project-URL: Repository, https://github.com/landparty/jcl-parser
9
- Project-URL: Documentation, https://github.com/landparty/jcl-parser/tree/main/docs
9
+ Project-URL: Documentation, https://github.com/landparty/jcl-parser/blob/main/.okf/index.md
10
10
  Project-URL: Bug Reports, https://github.com/landparty/jcl-parser/issues
11
11
  Keywords: jcl,mainframe,parser,ibm,job-control-language
12
12
  Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Intended Audience :: Developers
14
- Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
15
14
  Classifier: Operating System :: OS Independent
16
15
  Classifier: Programming Language :: Python :: 3
17
16
  Classifier: Programming Language :: Python :: 3.10
@@ -28,12 +27,14 @@ Requires-Dist: pytest>=7.0; extra == "dev"
28
27
  Requires-Dist: coverage[toml]>=7.0; extra == "dev"
29
28
  Requires-Dist: pytest-cov>=4.0; extra == "dev"
30
29
  Requires-Dist: pytest-benchmark>=4.0; extra == "dev"
31
- Requires-Dist: ruff>=0.1; extra == "dev"
30
+ Requires-Dist: ruff==0.17.3; extra == "dev"
32
31
  Requires-Dist: mypy>=1.0; extra == "dev"
32
+ Requires-Dist: jsonschema>=4.18; extra == "dev"
33
33
  Requires-Dist: tomli>=1.1.0; python_version < "3.11" and extra == "dev"
34
34
  Provides-Extra: test
35
35
  Requires-Dist: pytest>=7.0; extra == "test"
36
36
  Requires-Dist: pytest-cov>=4.0; extra == "test"
37
+ Requires-Dist: jsonschema>=4.18; extra == "test"
37
38
  Requires-Dist: tomli>=1.1.0; python_version < "3.11" and extra == "test"
38
39
  Provides-Extra: profiling
39
40
  Requires-Dist: psutil>=5.9; extra == "profiling"
@@ -50,7 +51,8 @@ Python library for parsing IBM z/OS JCL (Job Control Language) into structured `
50
51
  ## Install
51
52
 
52
53
  ```bash
53
- pip install -e ".[dev]"
54
+ pip install jcl-parser # from PyPI
55
+ pip install -e ".[dev]" # from a checkout, with the dev tools
54
56
  ```
55
57
 
56
58
  Requires Python 3.10+. No runtime dependencies.
@@ -58,11 +60,20 @@ Requires Python 3.10+. No runtime dependencies.
58
60
  ## Quick Start
59
61
 
60
62
  ```python
63
+ from pathlib import Path
64
+
61
65
  import jclparser
62
66
 
63
- job = jclparser.parse_jcl(source)
67
+ job = jclparser.parse_jcl(Path("PAYROLL.jcl")) # a str is always JCL text, never a path
64
68
  print(job.name)
65
- print(job.steps[0].program)
69
+ for step in jclparser.iter_execution_steps(job.steps): # also walks IF/THEN/ELSE blocks
70
+ print(step.name, step.program or step.procedure)
71
+
72
+ exploded = jclparser.explode_job(job) # PROC expansion is a separate call
73
+ errors = jclparser.validate_jcl(
74
+ Path("PAYROLL.jcl"), level="structure"
75
+ ) # list[ParseError]
76
+ print(job.to_json(indent=2)) # schema: jclparser.JOB_JSON_SCHEMA
66
77
  ```
67
78
 
68
79
  CLI:
@@ -71,7 +82,52 @@ CLI:
71
82
  jcl-parser FILE steps # list all steps
72
83
  jcl-parser FILE --validate # validate JCL
73
84
  jcl-parser FILE lint # run lint rules
74
- jcl-parser FILE --json # full parse result as JSON
85
+ jcl-parser FILE --json # full parse result as JSON (job.to_dict())
86
+ jcl-parser --json-schema # JSON Schema of that document
87
+ jcl-parser DIR # cross-job dataset lineage of a directory of jobs
88
+ jcl-parser DIR lineage mermaid # ... as a Mermaid graph (or `dot`)
89
+ jcl-parser DIR lint --sarif # lint every job of a directory, as SARIF
90
+ jcl-lint jobs/ --fail-on warning --format sarif -o jcl.sarif # CI / pre-commit
91
+ ```
92
+
93
+ ### Lint in CI
94
+
95
+ Configure the rules in `pyproject.toml`, and switch one off for one statement
96
+ with a comment:
97
+
98
+ ```toml
99
+ [tool.jcl-parser.lint]
100
+ ignore = ["JCL-LINT-002"]
101
+ fail-on = "warning"
102
+ [tool.jcl-parser.lint.severity]
103
+ "JCL-LINT-004" = "warning"
104
+ ```
105
+
106
+ ```jcl
107
+ //* jcl-lint: disable=JCL-LINT-001
108
+ //MASTER DD DSN=PROD.MASTER.FILE,DISP=SHR
109
+ ```
110
+
111
+ pre-commit (`.pre-commit-config.yaml`):
112
+
113
+ ```yaml
114
+ - repo: https://github.com/landparty/jcl-parser
115
+ rev: v2.2.0 # first release with the hook
116
+ hooks:
117
+ - id: jcl-lint
118
+ ```
119
+
120
+ GitHub Actions (findings appear in code scanning):
121
+
122
+ ```yaml
123
+ permissions:
124
+ security-events: write
125
+ steps:
126
+ - uses: actions/checkout@v4
127
+ - uses: landparty/jcl-parser@v2.2.0 # first release with the action
128
+ with:
129
+ paths: jcl
130
+ glob: "**/*.jcl"
75
131
  ```
76
132
 
77
133
  ## Commands
@@ -83,7 +139,7 @@ pytest -m unit
83
139
 
84
140
  # Lint / format / type-check
85
141
  ruff check . && ruff format .
86
- mypy jclparser/
142
+ python scripts/mypy_ratchet.py # mypy strict against the known-error baseline, as CI runs it
87
143
 
88
144
  # Vendor sync (after changes to jclparser/) — rm first: plain `cp -r` nests
89
145
  # into dest/jclparser/jclparser/ and leaves stale files when dest exists
@@ -92,7 +148,9 @@ rm -rf ../backend/_vendor/jcl-parser/jclparser && cp -r jclparser/ ../backend/_v
92
148
 
93
149
  ## Documentation
94
150
 
95
- Full reference: [`.okf/index.md`](.okf/index.md)
151
+ Full reference: [`.okf/index.md`](.okf/index.md). Contributors: start with [`CLAUDE.md`](CLAUDE.md)
152
+ (commands, architecture, invariants) and [`.okf/development/invariants.md`](.okf/development/invariants.md).
153
+ Changes are listed in [`CHANGELOG.md`](CHANGELOG.md) (releases) and [`.okf/log.md`](.okf/log.md).
96
154
 
97
155
  ## License
98
156
 
@@ -9,12 +9,14 @@ jcl_parser.egg-info/requires.txt
9
9
  jcl_parser.egg-info/top_level.txt
10
10
  jclparser/__init__.py
11
11
  jclparser/__main__.py
12
+ jclparser/_include_origins.py
12
13
  jclparser/_job_builder.py
13
14
  jclparser/_model_factories.py
14
15
  jclparser/_post_parse.py
15
16
  jclparser/_source_scanner.py
16
17
  jclparser/_statement_parser.py
17
18
  jclparser/builtin_procs.py
19
+ jclparser/checks.py
18
20
  jclparser/cli.py
19
21
  jclparser/condition_parser.py
20
22
  jclparser/continuation_handler.py
@@ -24,6 +26,7 @@ jclparser/explosion.py
24
26
  jclparser/include_loader.py
25
27
  jclparser/jcl_file_type.py
26
28
  jclparser/jes_statements.py
29
+ jclparser/lineage.py
27
30
  jclparser/metrics.py
28
31
  jclparser/model.py
29
32
  jclparser/parameter_ast.py
@@ -33,6 +36,7 @@ jclparser/proc_loader.py
33
36
  jclparser/profiling.py
34
37
  jclparser/py.typed
35
38
  jclparser/rc_analysis.py
39
+ jclparser/serialization.py
36
40
  jclparser/sql_detector.py
37
41
  jclparser/system_symbols.py
38
42
  jclparser/utils.py
@@ -42,6 +46,7 @@ jclparser/lint/base.py
42
46
  jclparser/lint/config.py
43
47
  jclparser/lint/rules.py
44
48
  jclparser/lint/runner.py
49
+ jclparser/lint/suppress.py
45
50
  jclparser/plugins/__init__.py
46
51
  jclparser/plugins/base.py
47
52
  jclparser/program_profiles/__init__.py
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
+ jcl-lint = jclparser.checks:lint_main
2
3
  jcl-parser = jclparser.cli:main
@@ -4,8 +4,9 @@ pytest>=7.0
4
4
  coverage[toml]>=7.0
5
5
  pytest-cov>=4.0
6
6
  pytest-benchmark>=4.0
7
- ruff>=0.1
7
+ ruff==0.17.3
8
8
  mypy>=1.0
9
+ jsonschema>=4.18
9
10
 
10
11
  [dev:python_version < "3.11"]
11
12
  tomli>=1.1.0
@@ -16,6 +17,7 @@ psutil>=5.9
16
17
  [test]
17
18
  pytest>=7.0
18
19
  pytest-cov>=4.0
20
+ jsonschema>=4.18
19
21
 
20
22
  [test:python_version < "3.11"]
21
23
  tomli>=1.1.0
@@ -20,7 +20,7 @@ application, for example::
20
20
  import logging as _logging
21
21
 
22
22
  # Package metadata
23
- __version__ = "2.0.3" # x-release-please-version
23
+ __version__ = "2.2.0" # x-release-please-version
24
24
  __author__ = "JCL Parser Team"
25
25
  __description__ = "Parse IBM JCL scripts into Python objects"
26
26
 
@@ -66,7 +66,9 @@ from .include_loader import (
66
66
  DictMemberLoader,
67
67
  IncludeMemberLoader,
68
68
  JCLLIBMemberLoader,
69
+ LineOrigin,
69
70
  resolve_includes,
71
+ resolve_includes_with_origins,
70
72
  )
71
73
  from .jcl_file_type import (
72
74
  FileType,
@@ -88,6 +90,15 @@ from .jes_statements import (
88
90
  XmitStatement,
89
91
  parse_jes_control,
90
92
  )
93
+ from .lineage import (
94
+ DatasetLineage,
95
+ DatasetUse,
96
+ JobDependency,
97
+ JobStream,
98
+ LineageReport,
99
+ build_lineage,
100
+ load_job_stream,
101
+ )
91
102
  from .lint import (
92
103
  LintConfig,
93
104
  LintRule,
@@ -100,7 +111,16 @@ from .lint import (
100
111
  to_sarif as lint_to_sarif,
101
112
  )
102
113
  from .metrics import JobMetrics, compute_metrics
103
- from .model import DDStatement, Job, OutputStatement, Parameter, Statement, Step
114
+ from .model import (
115
+ ConditionalBlock,
116
+ DDStatement,
117
+ Job,
118
+ OutputStatement,
119
+ Parameter,
120
+ Statement,
121
+ Step,
122
+ iter_execution_steps,
123
+ )
104
124
  from .parser import parse_jcl, parse_statement, validate_jcl
105
125
  from .plugins import (
106
126
  ParserConfig,
@@ -132,6 +152,12 @@ from .rc_analysis import (
132
152
  FeasibilityReport,
133
153
  compute_feasible_paths,
134
154
  )
155
+ from .serialization import (
156
+ JOB_JSON_SCHEMA,
157
+ SCHEMA_VERSION,
158
+ job_to_dict,
159
+ job_to_json,
160
+ )
135
161
  from .sql_detector import (
136
162
  DB2_PROGRAMS,
137
163
  SQLDetectionResult,
@@ -156,9 +182,6 @@ from .validators import (
156
182
  # default unless the consuming application configures handlers.
157
183
  _logging.getLogger(__name__).addHandler(_logging.NullHandler())
158
184
 
159
- # Initialize global plugin registry
160
- _registry = get_plugin_registry()
161
-
162
185
  # Public API - what gets imported with "from jclparser import *"
163
186
  __all__ = [ # noqa: RUF022 — grouped by API tier, not alphabetically
164
187
  # ── Tier 1: Stable public API ─────────────────────────────────────────────
@@ -172,6 +195,8 @@ __all__ = [ # noqa: RUF022 — grouped by API tier, not alphabetically
172
195
  "Parameter",
173
196
  "Statement",
174
197
  "OutputStatement",
198
+ "ConditionalBlock",
199
+ "iter_execution_steps",
175
200
  "JCLParseError",
176
201
  "JCLSyntaxError",
177
202
  "JCLValidationError",
@@ -230,7 +255,22 @@ __all__ = [ # noqa: RUF022 — grouped by API tier, not alphabetically
230
255
  "IncludeMemberLoader",
231
256
  "DictMemberLoader",
232
257
  "JCLLIBMemberLoader",
258
+ "LineOrigin",
233
259
  "resolve_includes",
260
+ "resolve_includes_with_origins",
261
+ # ── Tier 2: JSON export ───────────────────────────────────────────────────
262
+ "job_to_dict",
263
+ "job_to_json",
264
+ "JOB_JSON_SCHEMA",
265
+ "SCHEMA_VERSION",
266
+ # ── Tier 2: Cross-job dataset lineage ─────────────────────────────────────
267
+ "load_job_stream",
268
+ "build_lineage",
269
+ "JobStream",
270
+ "LineageReport",
271
+ "DatasetLineage",
272
+ "DatasetUse",
273
+ "JobDependency",
234
274
  # ── Tier 2: Dataset lifecycle analysis ───────────────────────────────────
235
275
  "DatasetEvent",
236
276
  "analyze_dataset_lifecycle",