jcl-parser 2.3.0__tar.gz → 2.3.1__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.
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/PKG-INFO +1 -1
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jcl_parser.egg-info/PKG-INFO +1 -1
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/__init__.py +1 -1
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/_job_builder.py +24 -4
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/_statement_parser.py +1 -1
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/explosion.py +3 -2
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/include_loader.py +39 -7
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/lineage.py +38 -2
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/pyproject.toml +1 -1
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/LICENSE +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/README.md +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jcl_parser.egg-info/SOURCES.txt +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jcl_parser.egg-info/dependency_links.txt +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jcl_parser.egg-info/entry_points.txt +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jcl_parser.egg-info/requires.txt +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jcl_parser.egg-info/top_level.txt +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/__main__.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/_include_origins.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/_model_factories.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/_post_parse.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/_source_scanner.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/builtin_procs.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/checks.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/cli.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/condition_parser.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/continuation_handler.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/dataset_lifecycle.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/errors.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/jcl_file_type.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/jes_statements.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/lint/__init__.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/lint/base.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/lint/config.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/lint/rules.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/lint/runner.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/lint/suppress.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/metrics.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/model.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/parameter_ast.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/parameter_validators.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/parser.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/plugins/__init__.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/plugins/base.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/proc_loader.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/profiling.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/__init__.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/_control.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/_invoked.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/adrdssu.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/base.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/binder.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/bpxbatch.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/compilers.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/db2_batch.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/dfsrrc00.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/dsnutilb.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/ftp.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/icetool.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/idcams.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/iebcompr.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/iebdg.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/iebgener.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/iebptpch.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/iebupdte.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/iefbr14.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/iehinitt.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/iehlist.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/iehprogm.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/ikjeft01.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/irxjcl.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/registry.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/program_profiles/sort.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/py.typed +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/rc_analysis.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/serialization.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/sql_detector.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/system_symbols.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/utils.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/jclparser/validators.py +0 -0
- {jcl_parser-2.3.0 → jcl_parser-2.3.1}/setup.cfg +0 -0
|
@@ -20,7 +20,7 @@ application, for example::
|
|
|
20
20
|
import logging as _logging
|
|
21
21
|
|
|
22
22
|
# Package metadata
|
|
23
|
-
__version__ = "2.3.
|
|
23
|
+
__version__ = "2.3.1" # x-release-please-version
|
|
24
24
|
__author__ = "JCL Parser Team"
|
|
25
25
|
__description__ = "Parse IBM JCL scripts into Python objects"
|
|
26
26
|
|
|
@@ -621,11 +621,31 @@ class JobBuilder:
|
|
|
621
621
|
sink.append(member)
|
|
622
622
|
# Found members are expanded before the parse loop runs, so an
|
|
623
623
|
# INCLUDE that reaches here could not be loaded.
|
|
624
|
-
self._warnings_sink().append(
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
624
|
+
self._warnings_sink().append(self._include_warning(stmt, str(member)))
|
|
625
|
+
|
|
626
|
+
def _include_warning(self, stmt: Statement, member: str) -> str:
|
|
627
|
+
"""The warning for an INCLUDE whose member could not be loaded."""
|
|
628
|
+
line = f"Line {stmt.line_number}: INCLUDE member"
|
|
629
|
+
missing = " its statements are missing from the job"
|
|
630
|
+
if "&" in member:
|
|
631
|
+
from .include_loader import _substitute_member_symbols
|
|
632
|
+
|
|
633
|
+
symbols = (
|
|
634
|
+
{**self.pending_sets, **self.job.set_statements}
|
|
635
|
+
if self.job
|
|
636
|
+
else self.pending_sets
|
|
628
637
|
)
|
|
638
|
+
resolved = _substitute_member_symbols(member, symbols)
|
|
639
|
+
if "&" in resolved:
|
|
640
|
+
# Not a missing member: its name could not be built (#180).
|
|
641
|
+
return (
|
|
642
|
+
f"{line} {member} uses a symbol with no SET value before "
|
|
643
|
+
f"it;{missing}"
|
|
644
|
+
)
|
|
645
|
+
member = f"{resolved} (from {member})"
|
|
646
|
+
return (
|
|
647
|
+
f"{line} {member} not found in the JCLLIB / procedure libraries;{missing}"
|
|
648
|
+
)
|
|
629
649
|
|
|
630
650
|
def _on_conditional(self, stmt: Statement, line_num: int) -> None:
|
|
631
651
|
# Conditional logic - track conditionals properly
|
|
@@ -803,7 +803,7 @@ def _extract_jcllib_order(content: str) -> list[str]:
|
|
|
803
803
|
symbols: dict[str, str] = {}
|
|
804
804
|
lines = split_source_lines(content)
|
|
805
805
|
for index, line in enumerate(lines):
|
|
806
|
-
_record_set_symbols(line.lstrip(), symbols)
|
|
806
|
+
_record_set_symbols(line.lstrip(), symbols, lines, index + 1)
|
|
807
807
|
params_part = _jcllib_operands(line)
|
|
808
808
|
if params_part is None:
|
|
809
809
|
continue
|
|
@@ -1898,8 +1898,9 @@ class _IncludeExpandingLoader(ProcedureLoader):
|
|
|
1898
1898
|
from .utils import split_source_lines
|
|
1899
1899
|
|
|
1900
1900
|
seen = dict(symbols) if symbols is not None else {}
|
|
1901
|
-
|
|
1902
|
-
|
|
1901
|
+
lines = split_source_lines(expanded_text)
|
|
1902
|
+
for index, line in enumerate(lines):
|
|
1903
|
+
_record_set_symbols(line.rstrip(), seen, lines, index + 1)
|
|
1903
1904
|
missing = _extract_include_member(line.rstrip())
|
|
1904
1905
|
if not missing:
|
|
1905
1906
|
continue
|
|
@@ -26,6 +26,7 @@ import errno
|
|
|
26
26
|
import logging
|
|
27
27
|
import os
|
|
28
28
|
from abc import ABC, abstractmethod
|
|
29
|
+
from collections.abc import Sequence
|
|
29
30
|
from pathlib import Path
|
|
30
31
|
from typing import NamedTuple
|
|
31
32
|
|
|
@@ -335,21 +336,22 @@ class _IncludeExpander:
|
|
|
335
336
|
for index, line in enumerate(lines):
|
|
336
337
|
expanded = None
|
|
337
338
|
if index not in data_indices:
|
|
338
|
-
expanded = self._expand_statement(
|
|
339
|
+
expanded = self._expand_statement(lines, index, depth, active)
|
|
339
340
|
if expanded is None:
|
|
340
341
|
expanded = [(line, LineOrigin(None, index + 1))]
|
|
341
342
|
result.extend(expanded)
|
|
342
343
|
return result
|
|
343
344
|
|
|
344
345
|
def _expand_statement(
|
|
345
|
-
self,
|
|
346
|
+
self, lines: list[str], index: int, depth: int, active: frozenset[str]
|
|
346
347
|
) -> list[_Entry] | None:
|
|
347
|
-
"""The expansion of
|
|
348
|
+
"""The expansion of ``lines[index]`` if it is a resolvable INCLUDE."""
|
|
349
|
+
line_number = index + 1
|
|
348
350
|
# Preserve leading whitespace: only strip the line terminator so that
|
|
349
351
|
# in-stream data lines (which may contain '//' after leading spaces)
|
|
350
352
|
# are never mistaken for JCL statements.
|
|
351
|
-
statement_line =
|
|
352
|
-
_record_set_symbols(statement_line, self.symbols)
|
|
353
|
+
statement_line = lines[index].rstrip("\r\n")
|
|
354
|
+
_record_set_symbols(statement_line, self.symbols, lines, index + 1)
|
|
353
355
|
member = _extract_include_member(statement_line)
|
|
354
356
|
if member is None:
|
|
355
357
|
return None
|
|
@@ -413,12 +415,19 @@ def _member_origin(origin: LineOrigin, member: str, include_line: int) -> LineOr
|
|
|
413
415
|
return LineOrigin(origin.member, include_line, origin.member_line)
|
|
414
416
|
|
|
415
417
|
|
|
416
|
-
def _record_set_symbols(
|
|
418
|
+
def _record_set_symbols(
|
|
419
|
+
line: str,
|
|
420
|
+
symbols: dict[str, str],
|
|
421
|
+
lines: Sequence[str] = (),
|
|
422
|
+
follow: int = 0,
|
|
423
|
+
) -> None:
|
|
417
424
|
"""Record the assignments of a ``// SET`` statement *line* into *symbols*.
|
|
418
425
|
|
|
419
426
|
Values are resolved against the symbols already recorded, mirroring the
|
|
420
427
|
parser's left-to-right SET evaluation. Lines that are not a SET statement
|
|
421
|
-
are ignored.
|
|
428
|
+
are ignored. A SET whose operand field ends in a comma is continued on the
|
|
429
|
+
following ``//`` records: *lines* from index *follow* on (the record after
|
|
430
|
+
*line*) are read for its remaining assignments (#180).
|
|
422
431
|
"""
|
|
423
432
|
if not line.startswith("//") or line.startswith("//*"):
|
|
424
433
|
return
|
|
@@ -436,6 +445,7 @@ def _record_set_symbols(line: str, symbols: dict[str, str]) -> None:
|
|
|
436
445
|
from .utils import is_positional_key, jcl_value_text
|
|
437
446
|
|
|
438
447
|
statement, _ = split_comment_field(line)
|
|
448
|
+
statement = _continue_operands(statement, lines, follow)
|
|
439
449
|
stmt = parse_statement(statement)
|
|
440
450
|
if stmt is None or stmt.type != "SET":
|
|
441
451
|
return
|
|
@@ -445,6 +455,28 @@ def _record_set_symbols(line: str, symbols: dict[str, str]) -> None:
|
|
|
445
455
|
symbols[key.upper()] = _resolve_set_value(jcl_value_text(value), symbols)
|
|
446
456
|
|
|
447
457
|
|
|
458
|
+
def _continue_operands(statement: str, lines: Sequence[str], follow: int) -> str:
|
|
459
|
+
"""Append the operands of the continuation records of *statement*.
|
|
460
|
+
|
|
461
|
+
A statement whose operand field ends in a comma continues on the next
|
|
462
|
+
``//`` record that has a blank in column 3; its operand field (up to the
|
|
463
|
+
first blank) is appended. ``//*`` comment records in between are skipped.
|
|
464
|
+
"""
|
|
465
|
+
from ._statement_parser import split_comment_field
|
|
466
|
+
|
|
467
|
+
statement = statement.rstrip()
|
|
468
|
+
while statement.endswith(",") and follow < len(lines):
|
|
469
|
+
cont = lines[follow].rstrip("\r\n")
|
|
470
|
+
follow += 1
|
|
471
|
+
if cont.startswith("//*"):
|
|
472
|
+
continue
|
|
473
|
+
if not cont.startswith("// ") or not cont[2:].strip():
|
|
474
|
+
break
|
|
475
|
+
operands, _ = split_comment_field("// " + cont[2:].lstrip())
|
|
476
|
+
statement += operands[2:].strip()
|
|
477
|
+
return statement
|
|
478
|
+
|
|
479
|
+
|
|
448
480
|
def _substitute_member_symbols(member: str, symbols: dict[str, str]) -> str:
|
|
449
481
|
"""Resolve ``&SYMBOL`` references in an INCLUDE member name.
|
|
450
482
|
|
|
@@ -22,7 +22,8 @@ share a load library or DBRMLIB do not depend on one another (issue #145). The
|
|
|
22
22
|
A DD's role comes from its ``DISP`` status: ``NEW`` / ``MOD`` write the
|
|
23
23
|
dataset, ``SHR`` / ``OLD`` read it (``OLD`` may also update it in place, which
|
|
24
24
|
DISP alone can't tell). A normal disposition of ``DELETE`` makes any status
|
|
25
|
-
but ``NEW`` a read (a delete runs after the dataset's producer
|
|
25
|
+
but ``NEW`` a read (a delete runs after the dataset's producer and after
|
|
26
|
+
every other job that reads or writes it), and
|
|
26
27
|
``(NEW,DELETE)`` -- a scratch dataset -- neither. Temporary datasets (``&&TEMP``), ``NULLFILE``,
|
|
27
28
|
``SYSOUT`` and DDs without a ``DISP`` are left out; a ``DSN=*.STEP.DD``
|
|
28
29
|
backward reference counts as the dataset it points at.
|
|
@@ -93,6 +94,9 @@ class DatasetUse:
|
|
|
93
94
|
dataset: str
|
|
94
95
|
disp: str
|
|
95
96
|
line_number: int = 0
|
|
97
|
+
#: The DD deletes the dataset (normal disposition ``DELETE``, any status
|
|
98
|
+
#: but ``NEW``); it is a read that must also follow every other use.
|
|
99
|
+
deletes: bool = False
|
|
96
100
|
|
|
97
101
|
def to_dict(self) -> dict[str, Any]:
|
|
98
102
|
return {
|
|
@@ -102,6 +106,7 @@ class DatasetUse:
|
|
|
102
106
|
"dataset": self.dataset,
|
|
103
107
|
"disp": self.disp,
|
|
104
108
|
"line_number": self.line_number,
|
|
109
|
+
"deletes": self.deletes,
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
|
|
@@ -131,7 +136,8 @@ class DatasetLineage:
|
|
|
131
136
|
|
|
132
137
|
@dataclass(frozen=True)
|
|
133
138
|
class JobDependency:
|
|
134
|
-
"""*consumer* reads datasets that *producer* writes
|
|
139
|
+
"""*consumer* reads datasets that *producer* writes, or deletes datasets
|
|
140
|
+
that *producer* also reads or writes (it must run after that job)."""
|
|
135
141
|
|
|
136
142
|
producer: str
|
|
137
143
|
consumer: str
|
|
@@ -257,6 +263,7 @@ def _record_uses(job_id: str, job: Job, datasets: dict[str, DatasetLineage]) ->
|
|
|
257
263
|
dataset=dsn,
|
|
258
264
|
disp=evt.disp_status,
|
|
259
265
|
line_number=evt.line_number,
|
|
266
|
+
deletes=evt.disp_normal == "DELETE" and evt.disp_status != "NEW",
|
|
260
267
|
)
|
|
261
268
|
role = _role(evt.disp_status, evt.disp_normal)
|
|
262
269
|
if role == "write":
|
|
@@ -313,12 +320,41 @@ def _dependencies(datasets: Mapping[str, DatasetLineage]) -> list[JobDependency]
|
|
|
313
320
|
labels = edges.setdefault((producer, consumer), [])
|
|
314
321
|
if label not in labels:
|
|
315
322
|
labels.append(label)
|
|
323
|
+
_add_delete_edges(datasets, edges)
|
|
316
324
|
return [
|
|
317
325
|
JobDependency(producer, consumer, tuple(sorted(keys)))
|
|
318
326
|
for (producer, consumer), keys in sorted(edges.items())
|
|
319
327
|
]
|
|
320
328
|
|
|
321
329
|
|
|
330
|
+
def _add_delete_edges(
|
|
331
|
+
datasets: Mapping[str, DatasetLineage], edges: dict[tuple[str, str], list[str]]
|
|
332
|
+
) -> None:
|
|
333
|
+
"""A job that deletes a dataset runs after every other job that uses it.
|
|
334
|
+
|
|
335
|
+
The producer edge is already there (a delete is a read); this adds one
|
|
336
|
+
from each other reader of the dataset -- or of its library or a member
|
|
337
|
+
of it -- so the deleter cannot be ordered before a job that still needs
|
|
338
|
+
the dataset (#179). Two jobs that each delete a dataset the other reads
|
|
339
|
+
depend on each other, a cycle.
|
|
340
|
+
"""
|
|
341
|
+
for deleted, lineage in datasets.items():
|
|
342
|
+
deleters = _unique(u.job for u in lineage.readers if u.deletes)
|
|
343
|
+
if not deleters:
|
|
344
|
+
continue
|
|
345
|
+
for used, other in datasets.items():
|
|
346
|
+
if used != deleted and not _feeds(used, deleted):
|
|
347
|
+
continue
|
|
348
|
+
# Label the edge with the more specific name (the member).
|
|
349
|
+
label = deleted if deleted != library_of(deleted) else used
|
|
350
|
+
for user in _unique([*other.reader_jobs, *other.writer_jobs]):
|
|
351
|
+
for deleter in deleters:
|
|
352
|
+
if user != deleter:
|
|
353
|
+
labels = edges.setdefault((user, deleter), [])
|
|
354
|
+
if label not in labels:
|
|
355
|
+
labels.append(label)
|
|
356
|
+
|
|
357
|
+
|
|
322
358
|
def _run_order(
|
|
323
359
|
jobs: list[str], dependencies: list[JobDependency]
|
|
324
360
|
) -> tuple[list[str], list[str]]:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "jcl-parser"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.1"
|
|
8
8
|
description = "Parse IBM JCL (Job Control Language) scripts into Python objects"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Samuel Dion", email = "sam94dion@gmail.com"}
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|