From e6caddfd44b20cb9b07d966e67f033d6fa8a8e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Dom=C3=ADnguez?= Date: Mon, 8 Mar 2021 17:05:45 +0100 Subject: [PATCH 01/15] Patch all imports of RDFLib. Has the drawback of also reflecting the changes on RDFLib when the user imports it by him/herself. Includes a little test to be performed once manually. --- osp/__init__.py | 48 +++++++++++++++++++++++++ osp/core/ontology/namespace_registry.py | 1 + osp/core/ontology/parser.py | 1 + osp/core/utils/general.py | 1 + tests/test_utils.py | 1 + 5 files changed, 52 insertions(+) diff --git a/osp/__init__.py b/osp/__init__.py index 69e3be50..53867b1f 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -1 +1,49 @@ __path__ = __import__('pkgutil').extend_path(__path__, __name__) + +# Patch RDFLib <= 5.0.0. See osp-core issue https://github.com/simphony/osp-core/issues/558 (the drive letter from the +# path is stripped on Windows by the graph.Graph.serialize method of RDFLib <= 5.0.0). +import rdflib +from urllib.parse import urlparse + + +def _compare_version_leq(version, other_version): + """Compares two software version strings. + + Receives two software version strings which are just numbers separated by dots and determines whether the first one + is less or equal than the second one. + + Args: + version (str): first version string (number separated by dots). + other_version (str) : second version string (number separated by dots). + + Returns: + bool: whether the first version string is less or equal than the second one. + """ + version = version.split('.') + other_version = other_version.split('.') + for i in range(0, min(len(version), len(other_version))): + if version[i] < other_version[i]: + return True + elif version[i] > other_version[i]: + return False + else: + if len(other_version) > len(version) and other_version[i + 1] > str(0): + return False + else: + return True + + +if _compare_version_leq(rdflib.__version__, '5.0.0'): + def graph_serialize_fix_decorator(func): + def graph_serialize(*args, **kwargs): + if kwargs.get('destination') is not None and not hasattr(kwargs.get('destination'), 'write'): + scheme, netloc, path, params, _query, fragment = urlparse('destination') + if scheme != 'file': + kwargs['destination'] += 'file:///' + func(*args, **kwargs) + return graph_serialize + rdflib.Graph.serialize = graph_serialize_fix_decorator(rdflib.Graph.serialize) + rdflib.Graph.serialize.patched = True +else: + rdflib.Graph.serialize.patched = False + diff --git a/osp/core/ontology/namespace_registry.py b/osp/core/ontology/namespace_registry.py index ac0dafdc..20902204 100644 --- a/osp/core/ontology/namespace_registry.py +++ b/osp/core/ontology/namespace_registry.py @@ -9,6 +9,7 @@ from osp.core.ontology.relationship import OntologyRelationship from osp.core.ontology.attribute import OntologyAttribute from functools import lru_cache +assert rdflib.Graph.serialize.patched is True logger = logging.getLogger(__name__) diff --git a/osp/core/ontology/parser.py b/osp/core/ontology/parser.py index 9a03062c..0fdc689a 100644 --- a/osp/core/ontology/parser.py +++ b/osp/core/ontology/parser.py @@ -8,6 +8,7 @@ import tempfile from osp.core.ontology.cuba import rdflib_cuba from osp.core.ontology.yml.yml_parser import YmlParser +assert rdflib.Graph.serialize.patched is True logger = logging.getLogger(__name__) diff --git a/osp/core/utils/general.py b/osp/core/utils/general.py index 96bcc31a..e5e86b4c 100644 --- a/osp/core/utils/general.py +++ b/osp/core/utils/general.py @@ -11,6 +11,7 @@ from osp.core.namespaces import cuba from rdflib_jsonld.parser import to_rdf as json_to_rdf from osp.core.ontology.datatypes import convert_from +assert rdflib.Graph.serialize.patched is True CUDS_IRI_PREFIX = "http://www.osp-core.com/cuds#" logger = logging.getLogger(__name__) diff --git a/tests/test_utils.py b/tests/test_utils.py index 2f2312da..409b327f 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -32,6 +32,7 @@ ) from osp.core.session.buffers import BufferContext from osp.core.cuds import Cuds +assert rdflib.Graph.serialize.patched is True try: from .test_session_city import TestWrapperSession From 9372be0155fca7f92df3a37cd4b41aff17b25ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Dom=C3=ADnguez?= Date: Mon, 8 Mar 2021 17:18:46 +0100 Subject: [PATCH 02/15] Removed the assert statements that tested the solution, as it seems to work. --- osp/__init__.py | 3 --- osp/core/ontology/namespace_registry.py | 1 - osp/core/ontology/parser.py | 1 - osp/core/utils/general.py | 1 - tests/test_utils.py | 1 - 5 files changed, 7 deletions(-) diff --git a/osp/__init__.py b/osp/__init__.py index 53867b1f..51f7328d 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -43,7 +43,4 @@ def graph_serialize(*args, **kwargs): func(*args, **kwargs) return graph_serialize rdflib.Graph.serialize = graph_serialize_fix_decorator(rdflib.Graph.serialize) - rdflib.Graph.serialize.patched = True -else: - rdflib.Graph.serialize.patched = False diff --git a/osp/core/ontology/namespace_registry.py b/osp/core/ontology/namespace_registry.py index 20902204..ac0dafdc 100644 --- a/osp/core/ontology/namespace_registry.py +++ b/osp/core/ontology/namespace_registry.py @@ -9,7 +9,6 @@ from osp.core.ontology.relationship import OntologyRelationship from osp.core.ontology.attribute import OntologyAttribute from functools import lru_cache -assert rdflib.Graph.serialize.patched is True logger = logging.getLogger(__name__) diff --git a/osp/core/ontology/parser.py b/osp/core/ontology/parser.py index 0fdc689a..9a03062c 100644 --- a/osp/core/ontology/parser.py +++ b/osp/core/ontology/parser.py @@ -8,7 +8,6 @@ import tempfile from osp.core.ontology.cuba import rdflib_cuba from osp.core.ontology.yml.yml_parser import YmlParser -assert rdflib.Graph.serialize.patched is True logger = logging.getLogger(__name__) diff --git a/osp/core/utils/general.py b/osp/core/utils/general.py index e5e86b4c..96bcc31a 100644 --- a/osp/core/utils/general.py +++ b/osp/core/utils/general.py @@ -11,7 +11,6 @@ from osp.core.namespaces import cuba from rdflib_jsonld.parser import to_rdf as json_to_rdf from osp.core.ontology.datatypes import convert_from -assert rdflib.Graph.serialize.patched is True CUDS_IRI_PREFIX = "http://www.osp-core.com/cuds#" logger = logging.getLogger(__name__) diff --git a/tests/test_utils.py b/tests/test_utils.py index 409b327f..2f2312da 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -32,7 +32,6 @@ ) from osp.core.session.buffers import BufferContext from osp.core.cuds import Cuds -assert rdflib.Graph.serialize.patched is True try: from .test_session_city import TestWrapperSession From ee2b4d90fd9b380789830d98519777a82284e2f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Dom=C3=ADnguez?= Date: Mon, 8 Mar 2021 17:25:28 +0100 Subject: [PATCH 03/15] Fixed small mistake. --- osp/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osp/__init__.py b/osp/__init__.py index 51f7328d..8af26aab 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -39,7 +39,7 @@ def graph_serialize(*args, **kwargs): if kwargs.get('destination') is not None and not hasattr(kwargs.get('destination'), 'write'): scheme, netloc, path, params, _query, fragment = urlparse('destination') if scheme != 'file': - kwargs['destination'] += 'file:///' + kwargs['destination'] = 'file:///' + kwargs['destination'] func(*args, **kwargs) return graph_serialize rdflib.Graph.serialize = graph_serialize_fix_decorator(rdflib.Graph.serialize) From 91abed9f05d0e738c67a802dcc248709c5cab1f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Dom=C3=ADnguez?= Date: Tue, 9 Mar 2021 09:47:27 +0100 Subject: [PATCH 04/15] Workaround: change paths to DOS device paths in Windows. --- osp/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osp/__init__.py b/osp/__init__.py index 8af26aab..f1b187b3 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -3,7 +3,9 @@ # Patch RDFLib <= 5.0.0. See osp-core issue https://github.com/simphony/osp-core/issues/558 (the drive letter from the # path is stripped on Windows by the graph.Graph.serialize method of RDFLib <= 5.0.0). import rdflib +import subprocess from urllib.parse import urlparse +import os def _compare_version_leq(version, other_version): @@ -37,9 +39,11 @@ def _compare_version_leq(version, other_version): def graph_serialize_fix_decorator(func): def graph_serialize(*args, **kwargs): if kwargs.get('destination') is not None and not hasattr(kwargs.get('destination'), 'write'): - scheme, netloc, path, params, _query, fragment = urlparse('destination') - if scheme != 'file': - kwargs['destination'] = 'file:///' + kwargs['destination'] + scheme, netloc, path, params, _query, fragment = urlparse(kwargs['destination']) + if urlparse(kwargs['destination']).path == r'\\': + output = subprocess.run(['mountvol', f'{scheme}:\\', '/L'], stdout=subprocess.PIPE) + dos_device_path = os.path.join(output.stdout.decode().strip().replace('?', '.'), path) + kwargs['destination'] = dos_device_path func(*args, **kwargs) return graph_serialize rdflib.Graph.serialize = graph_serialize_fix_decorator(rdflib.Graph.serialize) From 13876ed9cdcee5c3ff915c013b8ae85f0e3b9b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Dom=C3=ADnguez?= Date: Tue, 9 Mar 2021 13:27:09 +0100 Subject: [PATCH 05/15] Changed the method to obtain the DOS path from `subprocess.run` to the ctypes library and a win32 API call. Hopefully fixes the security issue the CI is referring to. --- osp/__init__.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/osp/__init__.py b/osp/__init__.py index f1b187b3..a2a3d47f 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -2,10 +2,11 @@ # Patch RDFLib <= 5.0.0. See osp-core issue https://github.com/simphony/osp-core/issues/558 (the drive letter from the # path is stripped on Windows by the graph.Graph.serialize method of RDFLib <= 5.0.0). -import rdflib -import subprocess -from urllib.parse import urlparse import os +import ctypes +import ctypes.wintypes as wintypes +from urllib.parse import urlparse +import rdflib def _compare_version_leq(version, other_version): @@ -36,15 +37,25 @@ def _compare_version_leq(version, other_version): if _compare_version_leq(rdflib.__version__, '5.0.0'): + # Then patch RDFLib with the following decorator. def graph_serialize_fix_decorator(func): def graph_serialize(*args, **kwargs): if kwargs.get('destination') is not None and not hasattr(kwargs.get('destination'), 'write'): + # Bug causing case. scheme, netloc, path, params, _query, fragment = urlparse(kwargs['destination']) - if urlparse(kwargs['destination']).path == r'\\': - output = subprocess.run(['mountvol', f'{scheme}:\\', '/L'], stdout=subprocess.PIPE) - dos_device_path = os.path.join(output.stdout.decode().strip().replace('?', '.'), path) + if urlparse(kwargs['destination']).path.startswith('\\'): # The destination is a windows path. + # Call the win32 API to get the volume ID. + windows_func = ctypes.windll.kernel32.GetVolumeNameForVolumeMountPointW + windows_func.argtypes = wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.DWORD + lpszVolumeMountPoint = wintypes.LPCWSTR(f'{scheme}:\\') + lpszVolumeName = ctypes.create_unicode_buffer(50) + cchBufferLength = wintypes.DWORD(50) + windows_func(lpszVolumeMountPoint, lpszVolumeName, cchBufferLength) + # Get a DOS_DEVICE_PATH, not affected by the drive letter stripping bug. + dos_device_path = os.path.join(lpszVolumeName.value.replace('?', '.') + or f'\\\\.\\Volume{{DRIVE_LETTER_{scheme}_NOT_ASSIGNED}}\\', + path) kwargs['destination'] = dos_device_path func(*args, **kwargs) return graph_serialize - rdflib.Graph.serialize = graph_serialize_fix_decorator(rdflib.Graph.serialize) - + rdflib.Graph.serialize = graph_serialize_fix_decorator(rdflib.Graph.serialize) \ No newline at end of file From e698e1675ccbd10c6a192eec2fd24b204c44f964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Dom=C3=ADnguez?= Date: Tue, 9 Mar 2021 13:37:20 +0100 Subject: [PATCH 06/15] Do not patch RDFLib on non Windows machines. --- osp/__init__.py | 114 ++++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 56 deletions(-) diff --git a/osp/__init__.py b/osp/__init__.py index a2a3d47f..d8217f88 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -2,60 +2,62 @@ # Patch RDFLib <= 5.0.0. See osp-core issue https://github.com/simphony/osp-core/issues/558 (the drive letter from the # path is stripped on Windows by the graph.Graph.serialize method of RDFLib <= 5.0.0). -import os -import ctypes -import ctypes.wintypes as wintypes -from urllib.parse import urlparse -import rdflib - - -def _compare_version_leq(version, other_version): - """Compares two software version strings. - - Receives two software version strings which are just numbers separated by dots and determines whether the first one - is less or equal than the second one. - - Args: - version (str): first version string (number separated by dots). - other_version (str) : second version string (number separated by dots). - - Returns: - bool: whether the first version string is less or equal than the second one. - """ - version = version.split('.') - other_version = other_version.split('.') - for i in range(0, min(len(version), len(other_version))): - if version[i] < other_version[i]: - return True - elif version[i] > other_version[i]: - return False - else: - if len(other_version) > len(version) and other_version[i + 1] > str(0): - return False +import sys +if sys.platform == 'win32': + import os + import ctypes + import ctypes.wintypes as wintypes + from urllib.parse import urlparse + import rdflib + + + def _compare_version_leq(version, other_version): + """Compares two software version strings. + + Receives two software version strings which are just numbers separated by dots and determines whether the first one + is less or equal than the second one. + + Args: + version (str): first version string (number separated by dots). + other_version (str) : second version string (number separated by dots). + + Returns: + bool: whether the first version string is less or equal than the second one. + """ + version = version.split('.') + other_version = other_version.split('.') + for i in range(0, min(len(version), len(other_version))): + if version[i] < other_version[i]: + return True + elif version[i] > other_version[i]: + return False else: - return True - - -if _compare_version_leq(rdflib.__version__, '5.0.0'): - # Then patch RDFLib with the following decorator. - def graph_serialize_fix_decorator(func): - def graph_serialize(*args, **kwargs): - if kwargs.get('destination') is not None and not hasattr(kwargs.get('destination'), 'write'): - # Bug causing case. - scheme, netloc, path, params, _query, fragment = urlparse(kwargs['destination']) - if urlparse(kwargs['destination']).path.startswith('\\'): # The destination is a windows path. - # Call the win32 API to get the volume ID. - windows_func = ctypes.windll.kernel32.GetVolumeNameForVolumeMountPointW - windows_func.argtypes = wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.DWORD - lpszVolumeMountPoint = wintypes.LPCWSTR(f'{scheme}:\\') - lpszVolumeName = ctypes.create_unicode_buffer(50) - cchBufferLength = wintypes.DWORD(50) - windows_func(lpszVolumeMountPoint, lpszVolumeName, cchBufferLength) - # Get a DOS_DEVICE_PATH, not affected by the drive letter stripping bug. - dos_device_path = os.path.join(lpszVolumeName.value.replace('?', '.') - or f'\\\\.\\Volume{{DRIVE_LETTER_{scheme}_NOT_ASSIGNED}}\\', - path) - kwargs['destination'] = dos_device_path - func(*args, **kwargs) - return graph_serialize - rdflib.Graph.serialize = graph_serialize_fix_decorator(rdflib.Graph.serialize) \ No newline at end of file + if len(other_version) > len(version) and other_version[i + 1] > str(0): + return False + else: + return True + + + if _compare_version_leq(rdflib.__version__, '5.0.0'): + # Then patch RDFLib with the following decorator. + def graph_serialize_fix_decorator(func): + def graph_serialize(*args, **kwargs): + if kwargs.get('destination') is not None and not hasattr(kwargs.get('destination'), 'write'): + # Bug causing case. + scheme, netloc, path, params, _query, fragment = urlparse(kwargs['destination']) + if urlparse(kwargs['destination']).path.startswith('\\'): # The destination is a windows path. + # Call the win32 API to get the volume ID. + windows_func = ctypes.windll.kernel32.GetVolumeNameForVolumeMountPointW + windows_func.argtypes = wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.DWORD + lpszVolumeMountPoint = wintypes.LPCWSTR(f'{scheme}:\\') + lpszVolumeName = ctypes.create_unicode_buffer(50) + cchBufferLength = wintypes.DWORD(50) + windows_func(lpszVolumeMountPoint, lpszVolumeName, cchBufferLength) + # Get a DOS_DEVICE_PATH, not affected by the drive letter stripping bug. + dos_device_path = os.path.join(lpszVolumeName.value.replace('?', '.') + or f'\\\\.\\Volume{{DRIVE_LETTER_{scheme}_NOT_ASSIGNED}}\\', + path) + kwargs['destination'] = dos_device_path + func(*args, **kwargs) + return graph_serialize + rdflib.Graph.serialize = graph_serialize_fix_decorator(rdflib.Graph.serialize) From 18a700ff3ee55c24556ea25e203bd9128a8a6775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Dom=C3=ADnguez?= Date: Tue, 9 Mar 2021 17:04:55 +0100 Subject: [PATCH 07/15] Clean osp namespace. --- osp/__init__.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/osp/__init__.py b/osp/__init__.py index d8217f88..c6eba43b 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -4,11 +4,11 @@ # path is stripped on Windows by the graph.Graph.serialize method of RDFLib <= 5.0.0). import sys if sys.platform == 'win32': - import os - import ctypes - import ctypes.wintypes as wintypes - from urllib.parse import urlparse - import rdflib + import os as _osp_os + import ctypes as _osp_ctypes + import ctypes.wintypes as _osp_wintypes + from urllib.parse import urlparse as _osp_urlparse + import rdflib as _osp_rdflib def _compare_version_leq(version, other_version): @@ -38,26 +38,26 @@ def _compare_version_leq(version, other_version): return True - if _compare_version_leq(rdflib.__version__, '5.0.0'): + if _compare_version_leq(_osp_rdflib.__version__, '5.0.0'): # Then patch RDFLib with the following decorator. - def graph_serialize_fix_decorator(func): + def _graph_serialize_fix_decorator(func): def graph_serialize(*args, **kwargs): if kwargs.get('destination') is not None and not hasattr(kwargs.get('destination'), 'write'): # Bug causing case. - scheme, netloc, path, params, _query, fragment = urlparse(kwargs['destination']) - if urlparse(kwargs['destination']).path.startswith('\\'): # The destination is a windows path. + scheme, netloc, path, params, _query, fragment = _osp_urlparse(kwargs['destination']) + if _osp_urlparse(kwargs['destination']).path.startswith('\\'): # The destination is a windows path. # Call the win32 API to get the volume ID. - windows_func = ctypes.windll.kernel32.GetVolumeNameForVolumeMountPointW - windows_func.argtypes = wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.DWORD - lpszVolumeMountPoint = wintypes.LPCWSTR(f'{scheme}:\\') - lpszVolumeName = ctypes.create_unicode_buffer(50) - cchBufferLength = wintypes.DWORD(50) + windows_func = _osp_ctypes.windll.kernel32.GetVolumeNameForVolumeMountPointW + windows_func.argtypes = _osp_wintypes.LPCWSTR, _osp_wintypes.LPWSTR, _osp_wintypes.DWORD + lpszVolumeMountPoint = _osp_wintypes.LPCWSTR(f'{scheme}:\\') + lpszVolumeName = _osp_ctypes.create_unicode_buffer(50) + cchBufferLength = _osp_wintypes.DWORD(50) windows_func(lpszVolumeMountPoint, lpszVolumeName, cchBufferLength) # Get a DOS_DEVICE_PATH, not affected by the drive letter stripping bug. - dos_device_path = os.path.join(lpszVolumeName.value.replace('?', '.') + dos_device_path = _osp_os.path.join(lpszVolumeName.value.replace('?', '.') or f'\\\\.\\Volume{{DRIVE_LETTER_{scheme}_NOT_ASSIGNED}}\\', path) kwargs['destination'] = dos_device_path func(*args, **kwargs) return graph_serialize - rdflib.Graph.serialize = graph_serialize_fix_decorator(rdflib.Graph.serialize) + _osp_rdflib.Graph.serialize = _graph_serialize_fix_decorator(_osp_rdflib.Graph.serialize) From 07a928937bd8aa5e61439dcac46e73de8cbad7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Dom=C3=ADnguez?= Date: Tue, 9 Mar 2021 17:11:01 +0100 Subject: [PATCH 08/15] Clean osp namespace. --- osp/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osp/__init__.py b/osp/__init__.py index c6eba43b..7a9d0dce 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -2,8 +2,8 @@ # Patch RDFLib <= 5.0.0. See osp-core issue https://github.com/simphony/osp-core/issues/558 (the drive letter from the # path is stripped on Windows by the graph.Graph.serialize method of RDFLib <= 5.0.0). -import sys -if sys.platform == 'win32': +import sys as _osp_sys +if _osp_sys.platform == 'win32': import os as _osp_os import ctypes as _osp_ctypes import ctypes.wintypes as _osp_wintypes From d2d3cd98ff134135e0ecc1e95e89fee04c7e4014 Mon Sep 17 00:00:00 2001 From: Matthias Urban Date: Wed, 10 Mar 2021 08:24:04 +0100 Subject: [PATCH 09/15] removed deprecation warnings --- osp/core/cuds.py | 2 +- osp/core/ontology/namespace_registry.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osp/core/cuds.py b/osp/core/cuds.py index 729ee038..2da62b46 100644 --- a/osp/core/cuds.py +++ b/osp/core/cuds.py @@ -120,7 +120,7 @@ def _neighbors(self): @property def _stored(self): - return self._graph is self.session.graph + return self.session is not None and self._graph is self.session.graph def get_triples(self, include_neighbor_types=False): """Get the triples of the cuds object.""" diff --git a/osp/core/ontology/namespace_registry.py b/osp/core/ontology/namespace_registry.py index ac0dafdc..6aa8b876 100644 --- a/osp/core/ontology/namespace_registry.py +++ b/osp/core/ontology/namespace_registry.py @@ -250,7 +250,7 @@ def _get_entity_name(self, entity_iri, ns_iri): x = self._graph.value(entity_iri, rdflib.SKOS.prefLabel) if x is not None: return x.toPython() - logger.warn(f"No label for {entity_iri}") + logger.warning(f"No label for {entity_iri}") return entity_iri[len(ns_iri):] def clear(self): From bf30bc5fad2122a81673eb91da42622e74ce0520 Mon Sep 17 00:00:00 2001 From: Matthias Urban Date: Wed, 10 Mar 2021 08:49:39 +0100 Subject: [PATCH 10/15] updated compare version --- osp/__init__.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/osp/__init__.py b/osp/__init__.py index 7a9d0dce..a801b51d 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -24,18 +24,15 @@ def _compare_version_leq(version, other_version): Returns: bool: whether the first version string is less or equal than the second one. """ - version = version.split('.') - other_version = other_version.split('.') - for i in range(0, min(len(version), len(other_version))): - if version[i] < other_version[i]: - return True - elif version[i] > other_version[i]: - return False - else: - if len(other_version) > len(version) and other_version[i + 1] > str(0): - return False - else: - return True + version = map(int, version.split('.')) + other_version = map(int, other_version.split('.')) + for v, o in zip(version, other_version): + if v == o: + continue + return v < o + + # check remaining numbers versions of other_version + return all(o <= 0 for o in other_version) if _compare_version_leq(_osp_rdflib.__version__, '5.0.0'): From 6f3d6bfe2037e756ce8ca0c02f3bcbb836cda7cd Mon Sep 17 00:00:00 2001 From: Matthias Urban Date: Wed, 10 Mar 2021 08:54:34 +0100 Subject: [PATCH 11/15] fixed grammar in in-line comment --- osp/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osp/__init__.py b/osp/__init__.py index a801b51d..636251ff 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -31,7 +31,7 @@ def _compare_version_leq(version, other_version): continue return v < o - # check remaining numbers versions of other_version + # check remaining numbers of other_version return all(o <= 0 for o in other_version) From 45d68bcccfe8917bb4f2dc087e7c13c55da0af1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Dom=C3=ADnguez?= Date: Wed, 10 Mar 2021 11:12:54 +0100 Subject: [PATCH 12/15] Added unit tests for the version comparison function. --- tests/test_rdflib_patch.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/test_rdflib_patch.py diff --git a/tests/test_rdflib_patch.py b/tests/test_rdflib_patch.py new file mode 100644 index 00000000..20ffae0f --- /dev/null +++ b/tests/test_rdflib_patch.py @@ -0,0 +1,37 @@ +"""Tests the patch applied to RDFLib <= 5.0.0 in `osp/__init__.py`. + +See osp-core issue https://github.com/simphony/osp-core/issues/558 (the drive +letter from the path is stripped on Windows by the graph.Graph.serialize method +of RDFLib <= 5.0.0). +""" + +import unittest +from osp import _compare_version_leq as compare_version_leq + + +class TestRDFLibPatch(unittest.TestCase): + """Test the RDFLib patch.""" + + def test_version_comparison(self): + """Test the version comparison function for a few version strings.""" + test_version_pairs = (('0.0.1', '0.0.1', True), + ('0.0.1', '0.0.1.0', True), + ('0.0.1.0', '0.0.1', True), + ('0.0.1', '0.0.1.0.0.0.19.0.23', True), + ('0.0.1.0.0.0.19.0.23', '0.0.1', False), + ('0.0.1', '0.0.5', True), + ('0.1', '0.0.5', False), + ('0.20', '5.1', True), + ) + + for version, other_version, expected_result in test_version_pairs: + with self.subTest(msg="Testing version {version} <= " + "{other_version}" + .format(version=version, + other_version=other_version)): + self.assertIs(compare_version_leq(version, other_version), + expected_result) + + +if __name__ == "__main__": + unittest.main() From 50598a989d4412d47a2b548dec977f7679e84b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Dom=C3=ADnguez?= Date: Wed, 10 Mar 2021 11:21:30 +0100 Subject: [PATCH 13/15] Fixed RDFLib patch test not running. --- osp/__init__.py | 50 +++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/osp/__init__.py b/osp/__init__.py index 636251ff..20c45101 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -3,6 +3,32 @@ # Patch RDFLib <= 5.0.0. See osp-core issue https://github.com/simphony/osp-core/issues/558 (the drive letter from the # path is stripped on Windows by the graph.Graph.serialize method of RDFLib <= 5.0.0). import sys as _osp_sys + + +def _compare_version_leq(version, other_version): + """Compares two software version strings. + + Receives two software version strings which are just numbers separated by dots and determines whether the first one + is less or equal than the second one. + + Args: + version (str): first version string (number separated by dots). + other_version (str) : second version string (number separated by dots). + + Returns: + bool: whether the first version string is less or equal than the second one. + """ + version = map(int, version.split('.')) + other_version = map(int, other_version.split('.')) + for v, o in zip(version, other_version): + if v == o: + continue + return v < o + + # check remaining numbers of other_version + return all(o <= 0 for o in other_version) + + if _osp_sys.platform == 'win32': import os as _osp_os import ctypes as _osp_ctypes @@ -11,30 +37,6 @@ import rdflib as _osp_rdflib - def _compare_version_leq(version, other_version): - """Compares two software version strings. - - Receives two software version strings which are just numbers separated by dots and determines whether the first one - is less or equal than the second one. - - Args: - version (str): first version string (number separated by dots). - other_version (str) : second version string (number separated by dots). - - Returns: - bool: whether the first version string is less or equal than the second one. - """ - version = map(int, version.split('.')) - other_version = map(int, other_version.split('.')) - for v, o in zip(version, other_version): - if v == o: - continue - return v < o - - # check remaining numbers of other_version - return all(o <= 0 for o in other_version) - - if _compare_version_leq(_osp_rdflib.__version__, '5.0.0'): # Then patch RDFLib with the following decorator. def _graph_serialize_fix_decorator(func): From 2e272a6fe3d1ba9b61e1dbc35c9a0e7a25854f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Dom=C3=ADnguez?= Date: Wed, 10 Mar 2021 11:27:20 +0100 Subject: [PATCH 14/15] Fixed line lengths (flake8 does not check __init__.py). --- osp/__init__.py | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/osp/__init__.py b/osp/__init__.py index 20c45101..a6d4231d 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -1,14 +1,17 @@ __path__ = __import__('pkgutil').extend_path(__path__, __name__) -# Patch RDFLib <= 5.0.0. See osp-core issue https://github.com/simphony/osp-core/issues/558 (the drive letter from the -# path is stripped on Windows by the graph.Graph.serialize method of RDFLib <= 5.0.0). +# Patch RDFLib <= 5.0.0. See osp-core issue +# https://github.com/simphony/osp-core/issues/558 (the drive letter from the +# path is stripped on Windows by the graph.Graph.serialize method of RDFLib +# <= 5.0.0). import sys as _osp_sys def _compare_version_leq(version, other_version): """Compares two software version strings. - Receives two software version strings which are just numbers separated by dots and determines whether the first one + Receives two software version strings which are just numbers separated by + dots and determines whether the first one is less or equal than the second one. Args: @@ -16,7 +19,8 @@ def _compare_version_leq(version, other_version): other_version (str) : second version string (number separated by dots). Returns: - bool: whether the first version string is less or equal than the second one. + bool: whether the first version string is less or equal than the second + one. """ version = map(int, version.split('.')) other_version = map(int, other_version.split('.')) @@ -36,27 +40,37 @@ def _compare_version_leq(version, other_version): from urllib.parse import urlparse as _osp_urlparse import rdflib as _osp_rdflib - if _compare_version_leq(_osp_rdflib.__version__, '5.0.0'): # Then patch RDFLib with the following decorator. def _graph_serialize_fix_decorator(func): def graph_serialize(*args, **kwargs): - if kwargs.get('destination') is not None and not hasattr(kwargs.get('destination'), 'write'): + if kwargs.get('destination') is not None \ + and not hasattr(kwargs.get('destination'), 'write'): # Bug causing case. - scheme, netloc, path, params, _query, fragment = _osp_urlparse(kwargs['destination']) - if _osp_urlparse(kwargs['destination']).path.startswith('\\'): # The destination is a windows path. + scheme, netloc, path, params, _query, fragment = \ + _osp_urlparse(kwargs['destination']) + # If the destination is a windows path. + if _osp_urlparse(kwargs['destination'])\ + .path.startswith('\\'): # Call the win32 API to get the volume ID. - windows_func = _osp_ctypes.windll.kernel32.GetVolumeNameForVolumeMountPointW - windows_func.argtypes = _osp_wintypes.LPCWSTR, _osp_wintypes.LPWSTR, _osp_wintypes.DWORD - lpszVolumeMountPoint = _osp_wintypes.LPCWSTR(f'{scheme}:\\') + windows_func = _osp_ctypes.windll.kernel32\ + .GetVolumeNameForVolumeMountPointW + windows_func.argtypes = _osp_wintypes.LPCWSTR,\ + _osp_wintypes.LPWSTR, _osp_wintypes.DWORD + lpszVolumeMountPoint = _osp_wintypes\ + .LPCWSTR(f'{scheme}:\\') lpszVolumeName = _osp_ctypes.create_unicode_buffer(50) cchBufferLength = _osp_wintypes.DWORD(50) - windows_func(lpszVolumeMountPoint, lpszVolumeName, cchBufferLength) - # Get a DOS_DEVICE_PATH, not affected by the drive letter stripping bug. - dos_device_path = _osp_os.path.join(lpszVolumeName.value.replace('?', '.') - or f'\\\\.\\Volume{{DRIVE_LETTER_{scheme}_NOT_ASSIGNED}}\\', - path) + windows_func(lpszVolumeMountPoint, lpszVolumeName, + cchBufferLength) + # Get a DOS_DEVICE_PATH, not affected by the drive + # letter stripping bug. + dos_device_path = _osp_os.path.join( + lpszVolumeName.value.replace('?', '.') + or f'\\\\.\\Volume{{DRIVE_LETTER_{scheme}_' + f'NOT_ASSIGNED}}\\', path) kwargs['destination'] = dos_device_path func(*args, **kwargs) return graph_serialize - _osp_rdflib.Graph.serialize = _graph_serialize_fix_decorator(_osp_rdflib.Graph.serialize) + _osp_rdflib.Graph.serialize = _graph_serialize_fix_decorator( + _osp_rdflib.Graph.serialize) From 56bcb29df105f0417e25900be5dab5df1a3334e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Dom=C3=ADnguez?= Date: Wed, 10 Mar 2021 11:32:42 +0100 Subject: [PATCH 15/15] Fixed _compare_version_leq function. --- osp/__init__.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/osp/__init__.py b/osp/__init__.py index a6d4231d..9af9bd3c 100644 --- a/osp/__init__.py +++ b/osp/__init__.py @@ -2,17 +2,18 @@ # Patch RDFLib <= 5.0.0. See osp-core issue # https://github.com/simphony/osp-core/issues/558 (the drive letter from the -# path is stripped on Windows by the graph.Graph.serialize method of RDFLib -# <= 5.0.0). +# path is stripped on Windows by the graph.Graph.serialize method of +# RDFLib <= 5.0.0). import sys as _osp_sys +import itertools as _osp_itertools def _compare_version_leq(version, other_version): """Compares two software version strings. Receives two software version strings which are just numbers separated by - dots and determines whether the first one - is less or equal than the second one. + dots and determines whether the first one is less or equal than the + second one. Args: version (str): first version string (number separated by dots). @@ -20,17 +21,17 @@ def _compare_version_leq(version, other_version): Returns: bool: whether the first version string is less or equal than the second - one. + one. """ version = map(int, version.split('.')) other_version = map(int, other_version.split('.')) - for v, o in zip(version, other_version): + for v, o in _osp_itertools.zip_longest(version, other_version, + fillvalue=0): if v == o: continue return v < o - - # check remaining numbers of other_version - return all(o <= 0 for o in other_version) + else: + return True if _osp_sys.platform == 'win32': @@ -67,8 +68,8 @@ def graph_serialize(*args, **kwargs): # letter stripping bug. dos_device_path = _osp_os.path.join( lpszVolumeName.value.replace('?', '.') - or f'\\\\.\\Volume{{DRIVE_LETTER_{scheme}_' - f'NOT_ASSIGNED}}\\', path) + or f'\\\\.\\Volume{{DRIVE_LETTER_{scheme}_NOT_' + f'ASSIGNED}}\\', path) kwargs['destination'] = dos_device_path func(*args, **kwargs) return graph_serialize