# HG changeset patch # User Oscar Curero # Date 1639946978 -3600 # Sun Dec 19 21:49:38 2021 +0100 # Node ID 5d29a12e375ee265c7f702b249af28553ffce514 # Parent 0000000000000000000000000000000000000000 initial version diff --git a/README.rst b/README.rst new file mode 100644 diff --git a/pyjed/__init__.py b/pyjed/__init__.py new file mode 100644 --- /dev/null +++ b/pyjed/__init__.py @@ -0,0 +1,7 @@ +from pyjed.jcl import (DD, + EXEC, + JOB, + ) + +__version__ = '0.1.0' +__all__ = ('DD', 'EXEC', 'JOB') diff --git a/pyjed/jcl.py b/pyjed/jcl.py new file mode 100644 --- /dev/null +++ b/pyjed/jcl.py @@ -0,0 +1,210 @@ +from collections import UserList +from dataclasses import dataclass, field, fields, make_dataclass + + +@dataclass +class Base: + def to_string(self, statement): + for parm in fields(self): + if (parm.name != 'name' and + getattr(self, parm.name) is not None and + parm.name not in self._attrs): + name, value = parm.name.upper().replace('_', ''), getattr(self, parm.name) + if isinstance(value, str) and ( + ' ' in value or [c for c in value if c.islower()]): + value = f"'{value}'" + if len(statement[-1]) + len(f'{name}={value},') > 78: + statement.append('// ') + statement[-1] = statement[-1] + f'{name}={value},' + print(statement) + statement[-1] = statement[-1][:-1] + return statement + + +@dataclass +class DISP: + status: str = '' + normal: str = '' + abnormal: str = '' + + +@dataclass +class DD(Base): + name: str + stream: str = field(default=None, repr=False) + accode: str = field(default=None, repr=False) + amp: str = field(default=None, repr=False) + avgrec: str = field(default=None, repr=False) + blksize: str = field(default=None, repr=False) + blkszlim: str = field(default=None, repr=False) + burst: str = field(default=None, repr=False) + ccsid: str = field(default=None, repr=False) + chars: str = field(default=None, repr=False) + chkpt: str = field(default=None, repr=False) + cntl: str = field(default=None, repr=False) + copies: str = field(default=None, repr=False) + data: str = field(default=None, repr=False) + dataclas: str = field(default=None, repr=False) + dcb: str = field(default=None, repr=False) + ddname: str = field(default=None, repr=False) + dest: str = field(default=None, repr=False) + disp: str = field(default=None) + dlm: str = field(default=None, repr=False) + dsid: str = field(default=None, repr=False) + dsname: str = field(default=None) + dsntype: str = field(default=None, repr=False) + dummy: str = field(default=None, repr=False) + dynam: str = field(default=None, repr=False) + eattr: str = field(default=None, repr=False) + expdt: str = field(default=None, repr=False) + fcb: str = field(default=None, repr=False) + filedata: str = field(default=None, repr=False) + flash: str = field(default=None, repr=False) + free: str = field(default=None, repr=False) + freevol: str = field(default=None, repr=False) + gdgorder: str = field(default=None, repr=False) + hold: str = field(default=None, repr=False) + keylabl1: str = field(default=None, repr=False) + keylabl2: str = field(default=None, repr=False) + keyencd1: str = field(default=None, repr=False) + keyencd2: str = field(default=None, repr=False) + keylen: str = field(default=None, repr=False) + keyoff: str = field(default=None, repr=False) + label: str = field(default=None, repr=False) + lgstream: str = field(default=None, repr=False) + like: str = field(default=None, repr=False) + lrecl: str = field(default=None, repr=False) + maxgens: str = field(default=None, repr=False) + mgmtclas: str = field(default=None, repr=False) + modify: str = field(default=None, repr=False) + outlim: str = field(default=None, repr=False) + output: str = field(default=None, repr=False) + path: str = field(default=None) + pathdisp: str = field(default=None, repr=False) + pathmode: str = field(default=None, repr=False) + pathopts: str = field(default=None, repr=False) + protect: str = field(default=None, repr=False) + recfm: str = field(default=None, repr=False) + recorg: str = field(default=None, repr=False) + refdd: str = field(default=None, repr=False) + retpd: str = field(default=None, repr=False) + rls: str = field(default=None, repr=False) + secmodel: str = field(default=None, repr=False) + segment: str = field(default=None, repr=False) + space: str = field(default=None, repr=False) + spin: str = field(default=None, repr=False) + storclas: str = field(default=None, repr=False) + subsys: str = field(default=None, repr=False) + symbols: str = field(default=None, repr=False) + symlist: str = field(default=None, repr=False) + sysout: str = field(default=None) + term: str = field(default=None, repr=False) + ucs: str = field(default=None, repr=False) + unit: str = field(default=None, repr=False) + volume: str = field(default=None) + + _attrs = ('stream') + _alias = {'VOLUME': 'VOL'} + + def to_string(self): + if self.stream: + stream = super().to_string([f'//{self.name} DD * ']) + stream.append(self.stream) + return stream + else: + return super().to_string([f'//{self.name} DD ']) + + +@dataclass +class COMMENT: + text: str = '' + + +@dataclass +class STEP(Base): + name: str + dd: list = field(default_factory=list) + _attrs = ('dd') + + def to_string(self): + string = super().to_string([f'//{self.name} EXEC ']) + for dd in self.dd: + string = string + dd.to_string() + print(string) + print('KIKO IKIKO IKIKO IKOKI') + return string + + +class EXEC(): + + @dataclass + class EXEC(STEP): + acct: str = field(default=None, repr=False) + addrspc: str = field(default=None, repr=False) + ccsid: str = field(default=None, repr=False) + cond: str = field(default=None, repr=False) + dynamnbr: str = field(default=None, repr=False) + memlimit: str = field(default=None, repr=False) + parm: str = field(default=None) + parmdd: str = field(default=None, repr=False) + perform: str = field(default=None, repr=False) + pgm: str = field(default=None) + proc: str = field(default=None) + rd: str = field(default=None, repr=False) + region: str = field(default=None, repr=False) + rlstmout: str = field(default=None, repr=False) + time: str = field(default=None, repr=False) + steplib: DD = field(default=None) + + def __new__(self, *args, **kwargs): + if len(args) == 2 or 'procname' in kwargs: + name = args[1] if len(args) == 2 else kwargs.pop('procname') + proc_parms = [(parm, str, field(default=None)) for parm in kwargs] + proc_class = make_dataclass(name, proc_parms, bases=(STEP,)) + return proc_class(name, **kwargs) + else: + return EXEC.EXEC(*args, **kwargs) + + +@dataclass() +class JOB(UserList, Base): + name: str + _class: str = None + msgclass: str = None + notify: str = None + user: str = None + joblib: DD = None + typrun: str = None + time: str = None + restart: str = None + password: str = None + prty: int = field(default=None, repr=False) + perform: str = field(default=None, repr=False) + schenv: str = field(default=None, repr=False) + sysaff: str = field(default=None, repr=False) + ujobcorr: str = field(default=None, repr=False) + seclabel: str = field(default=None, repr=False) + rd: str = field(default=None, repr=False) + pages: str = field(default=None, repr=False) + lines: str = field(default=None, repr=False) + jobrc: str = field(default=None, repr=False) + jeslog: str = field(default=None, repr=False) + group: str = field(default=None, repr=False) + dsenqshr: str = field(default=None, repr=False) + cond: str = field(default=None, repr=False) + ccsid: str = field(default=None, repr=False) + cards: str = field(default=None, repr=False) + bytes: str = field(default=None, repr=False) + addrspc: str = field(default=None, repr=False) + + _attrs = ('steps') + + def __post_init__(self): + super().__init__() + + def to_string(self): + stream = super().to_string([f'//{self.name} JOB ']) + for statement in self.data: + stream.extend(statement.to_string()) + print(stream) + return '\n'.join(stream) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,16 @@ +[tool.poetry] +name = "pyjed" +version = "0.1.0" +description = "" +authors = ["Your Name "] + +[tool.poetry.dependencies] +python = "^3.6" +dataclasses = { version="^0.8", python = "~3.6"} + +[tool.poetry.dev-dependencies] +pytest = "^5.2" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 diff --git a/tests/test_pyjed.py b/tests/test_pyjed.py new file mode 100644 --- /dev/null +++ b/tests/test_pyjed.py @@ -0,0 +1,98 @@ +# import pytest +from pyjed import __version__ +from pyjed import DD, EXEC, JOB + + +def test_version(): + assert __version__ == '0.1.0' + + +def test_job(): + JOB('MYJOB', _class='S') + + +def test_serialize_job_one_line(): + job = JOB('MYJOB', _class='S') + assert job.to_string() == '//MYJOB JOB CLASS=S' + + +def test_serialize_job_two_lines(): + job = JOB('MYJOB', _class='S', notify='&SYSUID', msgclass='V', time=34, user='JOHN', + schenv='SYS1', perform=244, prty=3) + assert job.to_string() == ('//MYJOB JOB CLASS=S,MSGCLASS=V,NOTIFY=&SYSUID,' + 'USER=JOHN,TIME=34,PRTY=3,\n// PERFORM=244,' + 'SCHENV=SYS1') + + +def test_serialize_job_with_step(): + job = JOB('MYJOB', _class='S') + job.append(EXEC('STEP1', pgm='IEFBR14')) + assert job.to_string() == '//MYJOB JOB CLASS=S\n//STEP1 EXEC PGM=IEFBR14' + + +def test_exec(): + EXEC('STEP1') + + +def test_exec_proc_arg(): + proc = EXEC('STEP', 'PROC1', test=1, foo=2) + assert proc.test == 1 and proc.foo == 2 + + +def test_exec_proc_kwarg(): + proc = EXEC('STEP', procname='PROC1', test=1, foo=2) + assert proc.test == 1 and proc.foo == 2 + + +def test_serialize_exec_one_line(): + exec = EXEC('STEP1', pgm='IEFBR14') + assert exec.to_string() == ['//STEP1 EXEC PGM=IEFBR14'] + + +def test_serialize_exec_one_line_with_whitespace_parm(): + exec = EXEC('STEP1', pgm='IEFBR14', parm='HELLO WORLD') + assert exec.to_string() == ['//STEP1 EXEC PARM=\'HELLO WORLD\',PGM=IEFBR14'] + + +def test_serialize_exec_one_line_with_lowercase_parm(): + exec = EXEC('STEP1', pgm='IEFBR14', parm='hello world') + assert exec.to_string() == ['//STEP1 EXEC PARM=\'hello world\',PGM=IEFBR14'] + + +def test_serialize_exec_two_lines(): + exec = EXEC('STEP1', pgm='IEFBR14', parm='Lorem ipsum dolor sit amet, consectetur ' + 'adipiscing elit') + assert exec.to_string() == ['//STEP1 EXEC PARM=\'Lorem ipsum dolor sit amet, ' + 'consectetur adipiscing elit\',', + '// PGM=IEFBR14'] + + +def test_serialize_exec_proc(): + proc = EXEC('STEP', procname='PROC1', test=1, foo=2) + proc.to_string() + + +def test_dd_dsname(): + DD('MYDD', dsname='MY.DATASET') + + +def test_serialize_dd_dataset_one_line(): + dd = DD('MYDD', dsname='MY.DATASET') + assert dd.to_string() == ['//MYDD DD DSNAME=MY.DATASET'] + + +def test_serialize_dd_instream(): + dd = DD('MYDD', stream='Lorem ipsum dolor sit amet') + assert dd.to_string() == ['//MYDD DD *', 'Lorem ipsum dolor sit amet'] + + +def test_serialize_dd_dataset_two_lines(): + dd = DD('MYDD', dsname='MY.LONG.LONG.LONG.DATASET', volume='SER', + disp='(,CATLG,DELETE)', dataclas='DCLAS02') + assert dd.to_string() == ['//MYDD DD DATACLAS=DCLAS02,DISP=(,CATLG,DELETE),', + '// DSNAME=MY.LONG.LONG.LONG.DATASET,VOLUME=SER'] + + +def test_serialize_dd_unixpath_one_line(): + dd = DD('MYDD', path='/usr/applics/pay.time') + assert dd.to_string() == ['//MYDD DD PATH=\'/usr/applics/pay.time\'']