From 0c7a2775902e4123c2c0c3670097d83b804f06ed Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Thu, 10 Feb 2022 20:23:12 -0600 Subject: [PATCH 01/25] Added helper function to convert path object to string --- src/psij/utils.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/psij/utils.py diff --git a/src/psij/utils.py b/src/psij/utils.py new file mode 100644 index 00000000..2028bae2 --- /dev/null +++ b/src/psij/utils.py @@ -0,0 +1,14 @@ +from pathlib import Path +import sys + +def path_object_to_full_path( obj ) -> str : + p = None + if obj : + if type(obj).__name__ == "str" : + p = obj + elif type(obj).__name__ == "PosixPath" : + p = obj.as_posix() + else : + print(type(obj)) + sys.exit("This type " + type(obj).__name__ + " for a path is not supported, use pathlib instead") + return p \ No newline at end of file From d781d644b60d475687e0554a47f4f1d6a556cc83 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Thu, 10 Feb 2022 20:26:57 -0600 Subject: [PATCH 02/25] Added function to serialize object to dict --- src/psij/job_spec.py | 70 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/psij/job_spec.py b/src/psij/job_spec.py index c0c61514..120311f6 100644 --- a/src/psij/job_spec.py +++ b/src/psij/job_spec.py @@ -4,6 +4,8 @@ from psij.job_attributes import JobAttributes from psij.resource_spec import ResourceSpec +from psij.utils import path_object_to_full_path as o2p + class JobSpec(object): """A class to hold information about the characteristics of a :class:`~psij.Job`.""" @@ -77,3 +79,71 @@ def name(self) -> Optional[str]: return self.executable else: return self._name + + + def _init_job_spec_dict(self) -> dict : + """Returns jobspec structure as dict""" + + # convention : + # - if expected value is a string then the dict is initialized with an empty string + # - if the expected value is an object than the key is initialzied with None + + job_spec = { + 'name': '', + 'executable' : '', + 'arguments': [] , + 'directory' : None, + 'inherit_environment': True, + 'environment': {} , + 'stdin_path': None, + 'stdout_path': None, + 'stderr_path': None, + 'resources': None, + 'attributes': None + } + + return job_spec + + @property + def to_dict(self) -> dict : + + d = self._init_job_spec_dict() + + # Map properties to keys + d['name'] = self.name + d['executable'] = self.executable + d['arguments'] = self.arguments + d['directory'] = o2p(self.directory) + d['inherit_environment'] = self.inherit_environment + d['environment'] = self.environment + d['stdin_path'] = o2p(self.stdin_path) + d['stdout_path'] = o2p(self.stdout_path) + d['stderr_path'] = o2p(self.stderr_path) + d['resources'] = self.resources + + # Handle attributes property + if self.attributes : + d['attributes'] = { + 'duration' : '' , + 'queue_name' : '' , + 'project_name' : '', + 'reservation_id' : '', + 'custom_attributes' : {} , + } + for k,v in self.attributes.__dict__.items() : + print(k,v) + if k in [ 'duration' , 'queue_name' , 'project_name' , 'reservation_id'] : + d['attributes'][k] = str(v) + elif k == "_custom_attributes" : + if v : + for ck,cv in obj.attributes._custom_attributes.items() : + if not type(cv).__name__ in ['str' , 'list' , 'dict' , 'NoneType' , 'bool' , 'int'] : + sys.stderr.write("Unsupported type " + type(cv).__name__ + " in JobAttributes.custom_attributes for key " + ck + ", skipping\n") + else : + d['attributes']['custom_attributes'][ck] = cv + else : + sys.stderr.write("Unsupported attribute " + k + ", skipping attribute\n") + else: + d['attributes'] = None + + return d \ No newline at end of file From a04bb47b13c487153990b97281d93339bdf3c297 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Thu, 10 Feb 2022 20:28:56 -0600 Subject: [PATCH 03/25] Initial stub for serialization class --- src/psij/serialize.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/psij/serialize.py diff --git a/src/psij/serialize.py b/src/psij/serialize.py new file mode 100644 index 00000000..bef1819b --- /dev/null +++ b/src/psij/serialize.py @@ -0,0 +1,33 @@ +from pathlib import Path +from typing import Optional, List, Dict +import sys + +class Export(object): + """A class for exporting psij data types.""" + + def __init__(self) : self.version = '' + + + @property + def name(self) -> Optional[str]: + """Returns the name of the job.""" + if self._name is None: + return self.executable + else: + return self._name + + + def to_dict(self,obj) -> dict : + + new_dict = {} + + if type(obj).__name__ in ["JobSpec"] : + new_dict = obj.to_dict + else: + sys.exit("Can't create dict, type " + type(obj).__name__ + " not supported" ) + + return new_dict + + +class Import() : + pass \ No newline at end of file From dc0ed9c19ea4580f40ef849d25e2b919c3a49cd9 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 10:32:47 -0600 Subject: [PATCH 04/25] Added import class and from_dict method --- src/psij/serialize.py | 47 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/psij/serialize.py b/src/psij/serialize.py index bef1819b..5bcadb5b 100644 --- a/src/psij/serialize.py +++ b/src/psij/serialize.py @@ -1,5 +1,7 @@ from pathlib import Path from typing import Optional, List, Dict +from psij.job_spec import JobSpec +from psij.job_attributes import JobAttributes import sys class Export(object): @@ -30,4 +32,47 @@ def to_dict(self,obj) -> dict : class Import() : - pass \ No newline at end of file + + + def _dict2spec(self,d) : + + # Initial spec object + spec = JobSpec() + + # Map properties to keys + spec._name = d['name'] if 'name' in d else d['_name'] + spec.executable = d['executable'] + spec.arguments = d['arguments'] + + spec.directory = Path(d['directory']) if ('directory' in d) and d['directory'] else None + spec.inherit_environment = d['inherit_environment'] + spec.environment = d['environment'] + spec.stdin_path = Path(d['stdin_path']) if ('stdin_path' in d) and d['stdin_path'] else None + spec.stdout_path = Path(d['stdout_path']) if ('stdout_path' in d) and d['stdout_path'] else None + spec.stderr_path = Path(d['stderr_path']) if ('stderr_path' in d) and d['stderr_path'] else None + spec.resources = d['resources'] + + # Handle attributes property + if d['attributes'] : + ja = JobAttributes() + + attributes = d['attributes'] + ja.duration = attributes['duration'] + ja.queue_name = attributes['queue_name'] + ja.reservation_id = attributes['reservation_id'] + ja.custom_attributes = attributes['custom_attributes'] + + spec.attributes = ja + print(spec) + return spec + + def from_dict(self,hash, target_type=None) : + + if target_type == "JobSpec" : + return(self._dict2spec(hash)) + else: + sys.exit("Can't create dict, type " + target_type + " not supported" ) + + + + \ No newline at end of file From 1230911e206530a0d755d0478139dc7fdd3d6a86 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 10:47:44 -0600 Subject: [PATCH 05/25] Added export to file method --- src/psij/serialize.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/psij/serialize.py b/src/psij/serialize.py index 5bcadb5b..51bbbd73 100644 --- a/src/psij/serialize.py +++ b/src/psij/serialize.py @@ -18,6 +18,17 @@ def name(self) -> Optional[str]: else: return self._name + def envelope(self, type=None) -> dict : + + envelope={ + 'version' : 0.1 , + 'type' : type , + 'data' : None + } + + return envelope + + def to_dict(self,obj) -> dict : @@ -30,6 +41,26 @@ def to_dict(self,obj) -> dict : return new_dict + def export(self, obj=None, dest=None) -> bool : + + if not dest : + sys.exit("Cannot export, missing destinstion file") + if not obj : + sys.exit("Cannot export, missing object") + + source_type = type(obj).__name__ + d = self.to_dict(obj) + + envelope = self.envelope(type=source_type) + envelope['data'] = d + + import json + with open(dest, 'w', encoding='utf-8') as f: + json.dump(envelope, f, ensure_ascii=False, indent=4) + + return True + + class Import() : From e44251c29aac469506c0eabd41542d3c821ae1b3 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 10:56:34 -0600 Subject: [PATCH 06/25] Added method to load JobSpec envelope from file --- src/psij/serialize.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/psij/serialize.py b/src/psij/serialize.py index 51bbbd73..cc2dc898 100644 --- a/src/psij/serialize.py +++ b/src/psij/serialize.py @@ -3,6 +3,7 @@ from psij.job_spec import JobSpec from psij.job_attributes import JobAttributes import sys +import json class Export(object): """A class for exporting psij data types.""" @@ -104,6 +105,18 @@ def from_dict(self,hash, target_type=None) : else: sys.exit("Can't create dict, type " + target_type + " not supported" ) - + def load(self, src=None) : + + if not src : + sys.exit("Cannot import, missing source file") + + + envelope=None + with open(src, 'r', encoding='utf-8') as f: + envelope = json.load(f) + + obj = self.from_dict(envelope['data'] , target_type=envelope['type']) + + return obj \ No newline at end of file From f6bc3ad19101ce7dd8fe65106497649979bbfee5 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 11:26:49 -0600 Subject: [PATCH 07/25] Added return types --- src/psij/serialize.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/psij/serialize.py b/src/psij/serialize.py index cc2dc898..ac61a433 100644 --- a/src/psij/serialize.py +++ b/src/psij/serialize.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Optional, List, Dict +from typing import Optional, List, Dict, Type, Any from psij.job_spec import JobSpec from psij.job_attributes import JobAttributes import sys @@ -66,7 +66,7 @@ def export(self, obj=None, dest=None) -> bool : class Import() : - def _dict2spec(self,d) : + def _dict2spec(self,d) -> Type[JobSpec] : # Initial spec object spec = JobSpec() @@ -98,14 +98,14 @@ def _dict2spec(self,d) : print(spec) return spec - def from_dict(self,hash, target_type=None) : + def from_dict(self,hash, target_type=None) -> Any : if target_type == "JobSpec" : return(self._dict2spec(hash)) else: sys.exit("Can't create dict, type " + target_type + " not supported" ) - def load(self, src=None) : + def load(self, src=None) -> Any : if not src : sys.exit("Cannot import, missing source file") From 066348a041793eb0add7759cab6ca36b363a6fb8 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 11:30:24 -0600 Subject: [PATCH 08/25] Added type package --- src/psij/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psij/utils.py b/src/psij/utils.py index 2028bae2..2ba8f05d 100644 --- a/src/psij/utils.py +++ b/src/psij/utils.py @@ -1,4 +1,5 @@ from pathlib import Path +from typing import Optional, List, Dict, Any, Type import sys def path_object_to_full_path( obj ) -> str : From a168ac019b9db0fddcc60bbf29c4722348ff8d8b Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 11:35:02 -0600 Subject: [PATCH 09/25] Added import of Type and Any from typing lib --- src/psij/job_spec.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/psij/job_spec.py b/src/psij/job_spec.py index 120311f6..1861d4c4 100644 --- a/src/psij/job_spec.py +++ b/src/psij/job_spec.py @@ -1,5 +1,6 @@ +import sys from pathlib import Path -from typing import Optional, List, Dict +from typing import Optional, List, Dict, Type, Any from psij.job_attributes import JobAttributes from psij.resource_spec import ResourceSpec From a014de55abfc4e77a0df1f2c26c11335b73cebc6 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 11:35:38 -0600 Subject: [PATCH 10/25] Deleted name method --- src/psij/serialize.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/psij/serialize.py b/src/psij/serialize.py index ac61a433..1c31f1e4 100644 --- a/src/psij/serialize.py +++ b/src/psij/serialize.py @@ -8,17 +8,11 @@ class Export(object): """A class for exporting psij data types.""" - def __init__(self) : self.version = '' + def __init__(self) : + self.version = '' + self.name = '' - @property - def name(self) -> Optional[str]: - """Returns the name of the job.""" - if self._name is None: - return self.executable - else: - return self._name - def envelope(self, type=None) -> dict : envelope={ From dcbfca90e2d590833b407eea35a1ea3dbaa9d1bc Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 11:51:12 -0600 Subject: [PATCH 11/25] Changed types to object --- src/psij/serialize.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/psij/serialize.py b/src/psij/serialize.py index 1c31f1e4..60d6b131 100644 --- a/src/psij/serialize.py +++ b/src/psij/serialize.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Optional, List, Dict, Type, Any +from typing import Optional, List, Dict, Type, Object from psij.job_spec import JobSpec from psij.job_attributes import JobAttributes import sys @@ -8,7 +8,7 @@ class Export(object): """A class for exporting psij data types.""" - def __init__(self) : + def __init__(self) -> Any : self.version = '' self.name = '' @@ -25,7 +25,7 @@ def envelope(self, type=None) -> dict : - def to_dict(self,obj) -> dict : + def to_dict(self, obj: object ) -> dict : new_dict = {} @@ -60,7 +60,7 @@ def export(self, obj=None, dest=None) -> bool : class Import() : - def _dict2spec(self,d) -> Type[JobSpec] : + def _dict2spec(self,d) -> object : # Initial spec object spec = JobSpec() @@ -92,14 +92,14 @@ def _dict2spec(self,d) -> Type[JobSpec] : print(spec) return spec - def from_dict(self,hash, target_type=None) -> Any : + def from_dict(self ,hash: dict , target_type=None) -> object : if target_type == "JobSpec" : return(self._dict2spec(hash)) else: sys.exit("Can't create dict, type " + target_type + " not supported" ) - def load(self, src=None) -> Any : + def load(self, src=None) -> object : if not src : sys.exit("Cannot import, missing source file") From 08c5a5f8fc305b764898fb4f227662f6e9abdb12 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 11:53:41 -0600 Subject: [PATCH 12/25] Changed types to object --- src/psij/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psij/utils.py b/src/psij/utils.py index 2ba8f05d..76ae4e5d 100644 --- a/src/psij/utils.py +++ b/src/psij/utils.py @@ -2,7 +2,7 @@ from typing import Optional, List, Dict, Any, Type import sys -def path_object_to_full_path( obj ) -> str : +def path_object_to_full_path( obj : Optional[object] ) -> str : p = None if obj : if type(obj).__name__ == "str" : From 18fd15c7a08293ca23b7d243ca90fa56ad2f73b1 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 12:00:23 -0600 Subject: [PATCH 13/25] Changed type checking --- src/psij/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psij/utils.py b/src/psij/utils.py index 76ae4e5d..d277634c 100644 --- a/src/psij/utils.py +++ b/src/psij/utils.py @@ -5,9 +5,9 @@ def path_object_to_full_path( obj : Optional[object] ) -> str : p = None if obj : - if type(obj).__name__ == "str" : + if isinstance(obj, str): : p = obj - elif type(obj).__name__ == "PosixPath" : + elif isinstance(obj, PosixPath): : p = obj.as_posix() else : print(type(obj)) From ddaca937945cd48b0470f4cd0de87ae45c0ce367 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 12:03:15 -0600 Subject: [PATCH 14/25] Removed error, additional ":" --- src/psij/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psij/utils.py b/src/psij/utils.py index d277634c..f8a28bbd 100644 --- a/src/psij/utils.py +++ b/src/psij/utils.py @@ -5,9 +5,9 @@ def path_object_to_full_path( obj : Optional[object] ) -> str : p = None if obj : - if isinstance(obj, str): : + if isinstance(obj, str): p = obj - elif isinstance(obj, PosixPath): : + elif isinstance(obj, PosixPath): p = obj.as_posix() else : print(type(obj)) From 0cac93a6d51df7a98fa8ec1d80fbeaf2e28c607f Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 12:07:24 -0600 Subject: [PATCH 15/25] Changed return type and type of checking --- src/psij/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/psij/utils.py b/src/psij/utils.py index f8a28bbd..6d2514ad 100644 --- a/src/psij/utils.py +++ b/src/psij/utils.py @@ -2,12 +2,12 @@ from typing import Optional, List, Dict, Any, Type import sys -def path_object_to_full_path( obj : Optional[object] ) -> str : - p = None +def path_object_to_full_path( obj : Optional[object] ) -> Optional[str] : + p = None if obj : if isinstance(obj, str): p = obj - elif isinstance(obj, PosixPath): + elif isinstance(obj, Path): p = obj.as_posix() else : print(type(obj)) From 91a7a9cf5c97b59b2bb7ca1df421a092c3fbfba6 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 12:39:21 -0600 Subject: [PATCH 16/25] Fixed error, wrong variable name --- src/psij/job_spec.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psij/job_spec.py b/src/psij/job_spec.py index 1861d4c4..5e878cca 100644 --- a/src/psij/job_spec.py +++ b/src/psij/job_spec.py @@ -88,7 +88,7 @@ def _init_job_spec_dict(self) -> dict : # convention : # - if expected value is a string then the dict is initialized with an empty string # - if the expected value is an object than the key is initialzied with None - + job_spec = {} job_spec = { 'name': '', 'executable' : '', @@ -137,7 +137,7 @@ def to_dict(self) -> dict : d['attributes'][k] = str(v) elif k == "_custom_attributes" : if v : - for ck,cv in obj.attributes._custom_attributes.items() : + for ck,cv in self.attributes._custom_attributes.items() : if not type(cv).__name__ in ['str' , 'list' , 'dict' , 'NoneType' , 'bool' , 'int'] : sys.stderr.write("Unsupported type " + type(cv).__name__ + " in JobAttributes.custom_attributes for key " + ck + ", skipping\n") else : From 0e2ce6ee72c8df6fe90f5b5af9825716965e4b92 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 12:40:12 -0600 Subject: [PATCH 17/25] Added types --- src/psij/serialize.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/psij/serialize.py b/src/psij/serialize.py index 60d6b131..7428df8f 100644 --- a/src/psij/serialize.py +++ b/src/psij/serialize.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Optional, List, Dict, Type, Object +from typing import Optional, List, Dict from psij.job_spec import JobSpec from psij.job_attributes import JobAttributes import sys @@ -8,12 +8,12 @@ class Export(object): """A class for exporting psij data types.""" - def __init__(self) -> Any : + def __init__(self) -> None : self.version = '' self.name = '' - def envelope(self, type=None) -> dict : + def envelope(self, type : Optional[str] = None) -> dict : envelope={ 'version' : 0.1 , @@ -29,14 +29,14 @@ def to_dict(self, obj: object ) -> dict : new_dict = {} - if type(obj).__name__ in ["JobSpec"] : + if isinstance(obj, JobSpec) : new_dict = obj.to_dict else: sys.exit("Can't create dict, type " + type(obj).__name__ + " not supported" ) return new_dict - def export(self, obj=None, dest=None) -> bool : + def export(self, obj : Optional[object] = None, dest : Optional[str] = None) -> bool : if not dest : sys.exit("Cannot export, missing destinstion file") @@ -49,7 +49,6 @@ def export(self, obj=None, dest=None) -> bool : envelope = self.envelope(type=source_type) envelope['data'] = d - import json with open(dest, 'w', encoding='utf-8') as f: json.dump(envelope, f, ensure_ascii=False, indent=4) From 9c2aa6b608485be0f0dae0f32680a8e2bff0734a Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 13:47:22 -0600 Subject: [PATCH 18/25] Added more typing --- src/psij/job_spec.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/psij/job_spec.py b/src/psij/job_spec.py index 5e878cca..1bc6a052 100644 --- a/src/psij/job_spec.py +++ b/src/psij/job_spec.py @@ -81,14 +81,14 @@ def name(self) -> Optional[str]: else: return self._name - - def _init_job_spec_dict(self) -> dict : + @property + def _init_job_spec_dict(self) -> Dict[str,Any] : """Returns jobspec structure as dict""" # convention : # - if expected value is a string then the dict is initialized with an empty string # - if the expected value is an object than the key is initialzied with None - job_spec = {} + job_spec : Dict[str,Any] job_spec = { 'name': '', 'executable' : '', @@ -106,9 +106,9 @@ def _init_job_spec_dict(self) -> dict : return job_spec @property - def to_dict(self) -> dict : + def to_dict(self) -> Dict[str,Any] : - d = self._init_job_spec_dict() + d = self._init_job_spec_dict # Map properties to keys d['name'] = self.name @@ -137,7 +137,7 @@ def to_dict(self) -> dict : d['attributes'][k] = str(v) elif k == "_custom_attributes" : if v : - for ck,cv in self.attributes._custom_attributes.items() : + for ck,cv in v.items() : if not type(cv).__name__ in ['str' , 'list' , 'dict' , 'NoneType' , 'bool' , 'int'] : sys.stderr.write("Unsupported type " + type(cv).__name__ + " in JobAttributes.custom_attributes for key " + ck + ", skipping\n") else : From 6ef4927adfe8a6376947125018d7b611f76c62a6 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 13:55:50 -0600 Subject: [PATCH 19/25] Added missing types --- src/psij/serialize.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/psij/serialize.py b/src/psij/serialize.py index 7428df8f..974dffc9 100644 --- a/src/psij/serialize.py +++ b/src/psij/serialize.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Optional, List, Dict +from typing import Optional, List, Dict , Any from psij.job_spec import JobSpec from psij.job_attributes import JobAttributes import sys @@ -13,7 +13,9 @@ def __init__(self) -> None : self.name = '' - def envelope(self, type : Optional[str] = None) -> dict : + def envelope(self, type : Optional[str] = None) -> Dict[str,Any] : + + envelope : Dict[str,Any] envelope={ 'version' : 0.1 , @@ -25,7 +27,7 @@ def envelope(self, type : Optional[str] = None) -> dict : - def to_dict(self, obj: object ) -> dict : + def to_dict(self, obj: object ) -> Dict[str,Any] : new_dict = {} @@ -59,7 +61,7 @@ def export(self, obj : Optional[object] = None, dest : Optional[str] = None) -> class Import() : - def _dict2spec(self,d) -> object : + def _dict2spec(self, d : Dict[str,Any]) -> object : # Initial spec object spec = JobSpec() @@ -85,20 +87,20 @@ def _dict2spec(self,d) -> object : ja.duration = attributes['duration'] ja.queue_name = attributes['queue_name'] ja.reservation_id = attributes['reservation_id'] - ja.custom_attributes = attributes['custom_attributes'] + ja._custom_attributes = attributes['custom_attributes'] spec.attributes = ja print(spec) return spec - def from_dict(self ,hash: dict , target_type=None) -> object : + def from_dict(self , hash: Dict[str,Any] , target_type : Optional[str] =None) -> object : if target_type == "JobSpec" : return(self._dict2spec(hash)) else: - sys.exit("Can't create dict, type " + target_type + " not supported" ) + sys.exit("Can't create dict, type " + str(target_type) + " not supported" ) - def load(self, src=None) -> object : + def load(self, src : Optional[str] = None) -> object : if not src : sys.exit("Cannot import, missing source file") From f6757ab72263e20e625e8a6edc737085b33c352e Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Fri, 11 Feb 2022 15:38:55 -0600 Subject: [PATCH 20/25] Changed formatting --- src/psij/job_spec.py | 121 +++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 61 deletions(-) diff --git a/src/psij/job_spec.py b/src/psij/job_spec.py index 1bc6a052..3bd1373b 100644 --- a/src/psij/job_spec.py +++ b/src/psij/job_spec.py @@ -1,15 +1,14 @@ import sys from pathlib import Path -from typing import Optional, List, Dict, Type, Any - +from typing import Optional, List, Dict, Any from psij.job_attributes import JobAttributes from psij.resource_spec import ResourceSpec -from psij.utils import path_object_to_full_path as o2p +from psij.utils import path_object_to_full_path as o2p class JobSpec(object): - """A class to hold information about the characteristics of a :class:`~psij.Job`.""" + """A class to hold information about the characteristics of a:class:`~psij.Job`.""" def __init__(self, name: Optional[str] = None, executable: Optional[str] = None, arguments: Optional[List[str]] = None, directory: Optional[Path] = None, @@ -21,38 +20,38 @@ def __init__(self, name: Optional[str] = None, executable: Optional[str] = None, """ Constructs a `JobSpec` object while allowing its properties to be initialized. - :param name: A name for the job. The name plays no functional role except that - :class:`~psij.JobExecutor` implementations may attempt to use the name to label the + :param name: A name for the job. The name plays no functional role except that + :class:`~psij.JobExecutor` implementations may attempt to use the name to label the job as presented by the underlying implementation. - :param executable: An executable, such as "/bin/date". - :param arguments: The argument list to be passed to the executable. Unlike with execve(), + :param executable: An executable, such as "/bin/date". + :param arguments: The argument list to be passed to the executable. Unlike with execve(), the first element of the list will correspond to `argv[1]` when accessed by the invoked executable. - :param directory: The directory, on the compute side, in which the executable is to be run - :param inherit_environment: If this flag is set to `False`, the job starts with an empty + :param directory: The directory, on the compute side, in which the executable is to be run + :param inherit_environment: If this flag is set to `False`, the job starts with an empty environment. The only environment variables that will be accessible to the job are the ones specified by this property. If this flag is set to `True`, which is the default, the job will also have access to variables inherited from the environment in which the job is run. - :param environment: A mapping of environment variable names to their respective values. - :param stdin_path: Path to a file whose contents will be sent to the job's standard input. - :param stdout_path: A path to a file in which to place the standard output stream of the + :param environment: A mapping of environment variable names to their respective values. + :param stdin_path: Path to a file whose contents will be sent to the job's standard input. + :param stdout_path: A path to a file in which to place the standard output stream of the job. - :param stderr_path: A path to a file in which to place the standard error stream of the job. - :param resources: The resource requirements specify the details of how the job is to be run + :param stderr_path: A path to a file in which to place the standard error stream of the job. + :param resources: The resource requirements specify the details of how the job is to be run on a cluster, such as the number and type of compute nodes used, etc. - :param attributes: Job attributes are details about the job, such as the walltime, that are + :param attributes: Job attributes are details about the job, such as the walltime, that are descriptive of how the job behaves. Attributes are, in principle, non-essential in that the job could run even though no attributes are specified. In practice, specifying a walltime is often necessary to prevent LRMs from prematurely terminating a job. - :param pre_launch: An optional path to a pre-launch script. The pre-launch script is + :param pre_launch: An optional path to a pre-launch script. The pre-launch script is sourced before the launcher is invoked. It, therefore, runs on the service node of the job rather than on all of the compute nodes allocated to the job. - :param post_launch: An optional path to a post-launch script. The post-launch script is + :param post_launch: An optional path to a post-launch script. The post-launch script is sourced after all the ranks of the job executable complete and is sourced on the same node as the pre-launch script. - :param launcher: The name of a launcher to use, such as "mpirun", "srun", "single", etc. - For a list of available launchers, :ref:`launchers` + :param launcher: The name of a launcher to use, such as "mpirun", "srun", "single", etc. + For a list of available launchers,:ref:`launchers` """ self._name = name self.executable = executable @@ -82,34 +81,35 @@ def name(self) -> Optional[str]: return self._name @property - def _init_job_spec_dict(self) -> Dict[str,Any] : + def _init_job_spec_dict(self) -> Dict[str, Any]: """Returns jobspec structure as dict""" - - # convention : + + # convention: # - if expected value is a string then the dict is initialized with an empty string # - if the expected value is an object than the key is initialzied with None - job_spec : Dict[str,Any] - job_spec = { - 'name': '', - 'executable' : '', - 'arguments': [] , - 'directory' : None, - 'inherit_environment': True, - 'environment': {} , - 'stdin_path': None, - 'stdout_path': None, - 'stderr_path': None, - 'resources': None, - 'attributes': None + + job_spec: Dict[str, Any] + job_spec = { + 'name': '', + 'executable': '', + 'arguments': [], + 'directory': None, + 'inherit_environment': True, + 'environment': {}, + 'stdin_path': None, + 'stdout_path': None, + 'stderr_path': None, + 'resources': None, + 'attributes': None } - + return job_spec @property - def to_dict(self) -> Dict[str,Any] : - + def to_dict(self) -> Dict[str, Any]: + d = self._init_job_spec_dict - + # Map properties to keys d['name'] = self.name d['executable'] = self.executable @@ -121,30 +121,29 @@ def to_dict(self) -> Dict[str,Any] : d['stdout_path'] = o2p(self.stdout_path) d['stderr_path'] = o2p(self.stderr_path) d['resources'] = self.resources - + # Handle attributes property - if self.attributes : + if self.attributes: d['attributes'] = { - 'duration' : '' , - 'queue_name' : '' , - 'project_name' : '', - 'reservation_id' : '', - 'custom_attributes' : {} , + 'duration': '', + 'queue_name': '', + 'project_name': '', + 'reservation_id': '', + 'custom_attributes': {}, } - for k,v in self.attributes.__dict__.items() : - print(k,v) - if k in [ 'duration' , 'queue_name' , 'project_name' , 'reservation_id'] : + for k, v in self.attributes.__dict__.items(): + if k in ['duration', 'queue_name', 'project_name', 'reservation_id']: d['attributes'][k] = str(v) - elif k == "_custom_attributes" : - if v : - for ck,cv in v.items() : - if not type(cv).__name__ in ['str' , 'list' , 'dict' , 'NoneType' , 'bool' , 'int'] : - sys.stderr.write("Unsupported type " + type(cv).__name__ + " in JobAttributes.custom_attributes for key " + ck + ", skipping\n") - else : - d['attributes']['custom_attributes'][ck] = cv - else : - sys.stderr.write("Unsupported attribute " + k + ", skipping attribute\n") + elif k == "_custom_attributes": + if v: + for ck, cv in v.items(): + if not type(cv).__name__ in ['str', 'list', 'dict', 'NoneType', 'bool', 'int']: + sys.stderr.write("Unsupported type " + type(cv).__name__ + " in JobAttributes.custom_attributes for key " + ck + ", skipping\n") + else: + d['attributes']['custom_attributes'][ck] = cv + else: + sys.stderr.write("Unsupported attribute " + k + ", skipping attribute\n") else: d['attributes'] = None - - return d \ No newline at end of file + + return d From 1ffe1506965bb86f0d1e5dcdb74a61efafcdb952 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Wed, 16 Feb 2022 10:00:28 -0600 Subject: [PATCH 21/25] Text formatting --- src/psij/serialize.py | 120 ++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 63 deletions(-) diff --git a/src/psij/serialize.py b/src/psij/serialize.py index 974dffc9..bc61c070 100644 --- a/src/psij/serialize.py +++ b/src/psij/serialize.py @@ -1,117 +1,111 @@ from pathlib import Path -from typing import Optional, List, Dict , Any +from typing import Optional, Dict, Any from psij.job_spec import JobSpec from psij.job_attributes import JobAttributes import sys import json + class Export(object): + """A class for exporting psij data types.""" - def __init__(self) -> None : + def __init__(self) -> None: self.version = '' self.name = '' + def envelope(self, type: Optional[str] = None) -> Dict[str, Any]: - def envelope(self, type : Optional[str] = None) -> Dict[str,Any] : - - envelope : Dict[str,Any] - - envelope={ - 'version' : 0.1 , - 'type' : type , - 'data' : None + envelope: Dict[str, Any] + + envelope = { + 'version': 0.1, + 'type': type, + 'data': None } - + return envelope - - - def to_dict(self, obj: object ) -> Dict[str,Any] : - + def to_dict(self, obj: object) -> Dict[str, Any]: + new_dict = {} - - if isinstance(obj, JobSpec) : + + if isinstance(obj, JobSpec): new_dict = obj.to_dict else: - sys.exit("Can't create dict, type " + type(obj).__name__ + " not supported" ) + sys.exit("Can't create dict, type " + type(obj).__name__ + " not supported") return new_dict - - def export(self, obj : Optional[object] = None, dest : Optional[str] = None) -> bool : - - if not dest : + + def export(self, obj: Optional[object] = None, dest: Optional[str] = None) -> bool: + + if not dest: sys.exit("Cannot export, missing destinstion file") - if not obj : + if not obj: sys.exit("Cannot export, missing object") - + source_type = type(obj).__name__ d = self.to_dict(obj) - + envelope = self.envelope(type=source_type) envelope['data'] = d - + with open(dest, 'w', encoding='utf-8') as f: json.dump(envelope, f, ensure_ascii=False, indent=4) - + return True - - - -class Import() : - - - def _dict2spec(self, d : Dict[str,Any]) -> object : - + + +class Import(): + + def _dict2spec(self, d: Dict[str, Any]) -> object: + # Initial spec object spec = JobSpec() - + # Map properties to keys - spec._name = d['name'] if 'name' in d else d['_name'] - spec.executable = d['executable'] + spec._name = d['name'] if 'name' in d else d['_name'] + spec.executable = d['executable'] spec.arguments = d['arguments'] - + spec.directory = Path(d['directory']) if ('directory' in d) and d['directory'] else None spec.inherit_environment = d['inherit_environment'] - spec.environment = d['environment'] + spec.environment = d['environment'] spec.stdin_path = Path(d['stdin_path']) if ('stdin_path' in d) and d['stdin_path'] else None spec.stdout_path = Path(d['stdout_path']) if ('stdout_path' in d) and d['stdout_path'] else None spec.stderr_path = Path(d['stderr_path']) if ('stderr_path' in d) and d['stderr_path'] else None - spec.resources = d['resources'] - + spec.resources = d['resources'] + # Handle attributes property - if d['attributes'] : + if d['attributes']: ja = JobAttributes() - - attributes = d['attributes'] + + attributes = d['attributes'] ja.duration = attributes['duration'] ja.queue_name = attributes['queue_name'] ja.reservation_id = attributes['reservation_id'] ja._custom_attributes = attributes['custom_attributes'] - + spec.attributes = ja print(spec) return spec - - def from_dict(self , hash: Dict[str,Any] , target_type : Optional[str] =None) -> object : - - if target_type == "JobSpec" : + + def from_dict(self, hash: Dict[str, Any], target_type: Optional[str] = None) -> object: + + if target_type == "JobSpec": return(self._dict2spec(hash)) else: - sys.exit("Can't create dict, type " + str(target_type) + " not supported" ) + sys.exit("Can't create dict, type " + str(target_type) + " not supported") + + def load(self, src: Optional[str] = None) -> object: - def load(self, src : Optional[str] = None) -> object : - - if not src : + if not src: sys.exit("Cannot import, missing source file") - - - envelope=None + + envelope = None with open(src, 'r', encoding='utf-8') as f: envelope = json.load(f) - - obj = self.from_dict(envelope['data'] , target_type=envelope['type']) - - return obj - - \ No newline at end of file + + obj = self.from_dict(envelope['data'], target_type=envelope['type']) + + return obj From 1289ded22469554414e2f22084fda46d0b7be9b4 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Wed, 16 Feb 2022 10:04:19 -0600 Subject: [PATCH 22/25] Added blank line --- src/psij/utils.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/psij/utils.py b/src/psij/utils.py index 6d2514ad..bc6afd84 100644 --- a/src/psij/utils.py +++ b/src/psij/utils.py @@ -1,15 +1,16 @@ from pathlib import Path -from typing import Optional, List, Dict, Any, Type +from typing import Optional import sys -def path_object_to_full_path( obj : Optional[object] ) -> Optional[str] : - p = None - if obj : + +def path_object_to_full_path(obj: Optional[object]) -> Optional[str]: + p = None + if obj: if isinstance(obj, str): p = obj elif isinstance(obj, Path): p = obj.as_posix() - else : + else: print(type(obj)) - sys.exit("This type " + type(obj).__name__ + " for a path is not supported, use pathlib instead") - return p \ No newline at end of file + sys.exit("This type " + type(obj).__name__ + " for a path is not supported, use pathlib instead") + return p From 33f5294c5eca5c2358b9f753386c30839fa69d54 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Wed, 16 Feb 2022 10:18:00 -0600 Subject: [PATCH 23/25] Formatting long line --- src/psij/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/psij/utils.py b/src/psij/utils.py index bc6afd84..f2e1a4ea 100644 --- a/src/psij/utils.py +++ b/src/psij/utils.py @@ -12,5 +12,6 @@ def path_object_to_full_path(obj: Optional[object]) -> Optional[str]: p = obj.as_posix() else: print(type(obj)) - sys.exit("This type " + type(obj).__name__ + " for a path is not supported, use pathlib instead") + sys.exit("This type " + type(obj).__name__ + + " for a path is not supported, use pathlib instead") return p From 7482b13d45bdba38242d2c3475f929c7986f331b Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Wed, 16 Feb 2022 10:22:43 -0600 Subject: [PATCH 24/25] Formatting long lines --- src/psij/serialize.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/psij/serialize.py b/src/psij/serialize.py index bc61c070..c562d9a7 100644 --- a/src/psij/serialize.py +++ b/src/psij/serialize.py @@ -71,9 +71,12 @@ def _dict2spec(self, d: Dict[str, Any]) -> object: spec.directory = Path(d['directory']) if ('directory' in d) and d['directory'] else None spec.inherit_environment = d['inherit_environment'] spec.environment = d['environment'] - spec.stdin_path = Path(d['stdin_path']) if ('stdin_path' in d) and d['stdin_path'] else None - spec.stdout_path = Path(d['stdout_path']) if ('stdout_path' in d) and d['stdout_path'] else None - spec.stderr_path = Path(d['stderr_path']) if ('stderr_path' in d) and d['stderr_path'] else None + spec.stdin_path = Path(d['stdin_path']) if ( + 'stdin_path' in d) and d['stdin_path'] else None + spec.stdout_path = Path(d['stdout_path']) if ( + 'stdout_path' in d) and d['stdout_path'] else None + spec.stderr_path = Path(d['stderr_path']) if ( + 'stderr_path' in d) and d['stderr_path'] else None spec.resources = d['resources'] # Handle attributes property From 2622916446dea097d030e67d225e98bcf678ad39 Mon Sep 17 00:00:00 2001 From: Andreas Wilke Date: Wed, 16 Feb 2022 10:26:12 -0600 Subject: [PATCH 25/25] Formatting long lines --- src/psij/job_spec.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/psij/job_spec.py b/src/psij/job_spec.py index 3bd1373b..5cec2868 100644 --- a/src/psij/job_spec.py +++ b/src/psij/job_spec.py @@ -137,8 +137,17 @@ def to_dict(self) -> Dict[str, Any]: elif k == "_custom_attributes": if v: for ck, cv in v.items(): - if not type(cv).__name__ in ['str', 'list', 'dict', 'NoneType', 'bool', 'int']: - sys.stderr.write("Unsupported type " + type(cv).__name__ + " in JobAttributes.custom_attributes for key " + ck + ", skipping\n") + if not type(cv).__name__ in ['str', + 'list', + 'dict', + 'NoneType', + 'bool', + 'int']: + sys.stderr.write("Unsupported type " + + type(cv).__name__ + + " in JobAttributes.custom_attributes for key " + + ck + + ", skipping\n") else: d['attributes']['custom_attributes'][ck] = cv else: