Skip to content

Commit

Permalink
Py 3.7 compatibility
Browse files Browse the repository at this point in the history
Lock on latest jsonasobj for integration
  • Loading branch information
hsolbrig committed Apr 28, 2021
1 parent 09651b9 commit 99f0e23
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 61 deletions.
8 changes: 8 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
CHANGES
=======

* Switch to a better behaved jsonasobj

v0.1.1
------

* Automated adding outputs from tests
* v0.1.0 tag

v0.1.0
------

Expand Down
5 changes: 3 additions & 2 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ name = "pypi"

[packages]
hbreader = "*"
# Note: pyld needs requests package to access contexts
requests = "*"
pyld = { git = "https://github.com/hsolbrig/pyld" }
jsonasobj = "==2.0.2dev1"
jsonasobj = ">=2.0.2dev4"
pyyaml = ">=5.1"
rdflib = "*"
rdflib-pyld-compat = "*"
Expand All @@ -16,7 +18,6 @@ prefixcommons = "*"
shexjsg = "*"

[dev-packages]
requests = "*"

[pipenv]
allow_prereleases = true
158 changes: 107 additions & 51 deletions linkml_runtime/utils/dataclass_extensions_376.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,107 @@
from dataclasses import MISSING, _HAS_DEFAULT_FACTORY, _POST_INIT_NAME, _FIELD_INITVAR, _init_param, _field_init, _create_fn


def dataclasses_init_fn_with_kwargs(fields, frozen, has_post_init, self_name, globals):
# fields contains both real fields and InitVar pseudo-fields.

# Make sure we don't have fields without defaults following fields
# with defaults. This actually would be caught when exec-ing the
# function source code, but catching it here gives a better error
# message, and future-proofs us in case we build up the function
# using ast.
seen_default = False
for f in fields:
# Only consider fields in the __init__ call.
if f.init:
if not (f.default is MISSING and f.default_factory is MISSING):
seen_default = True
elif seen_default:
raise TypeError(f'non-default argument {f.name!r} '
'follows default argument')

locals = {f'_type_{f.name}': f.type for f in fields}
locals.update({
'MISSING': MISSING,
'_HAS_DEFAULT_FACTORY': _HAS_DEFAULT_FACTORY,
})

body_lines = []
for f in fields:
line = _field_init(f, frozen, locals, self_name)
# line is None means that this field doesn't require
# initialization (it's a pseudo-field). Just skip it.
if line:
body_lines.append(line)

# Does this class have a post-init function?
if has_post_init:
params_str = ','.join(f.name for f in fields
if f._field_type is _FIELD_INITVAR)
body_lines.append(f'{self_name}.{_POST_INIT_NAME}({params_str}{", " if params_str else ""} **kwargs)')

# If no body lines, use 'pass'.
if not body_lines:
body_lines = ['pass']

return _create_fn('__init__',
[self_name] + [_init_param(f) for f in fields if f.init] + ["**kwargs"],
body_lines,
locals=locals,
globals=globals,
return_type=None)
import sys

if sys.version_info < (3, 7, 0):
raise NotImplementedError("LinkML requires Python 3.7 or later to run")
elif sys.version_info >= (3, 7, 6):
from dataclasses import MISSING, _HAS_DEFAULT_FACTORY, _POST_INIT_NAME, _FIELD_INITVAR, _init_param, _field_init, _create_fn


def dataclasses_init_fn_with_kwargs(fields, frozen, has_post_init, self_name, globals):
# fields contains both real fields and InitVar pseudo-fields.

# Make sure we don't have fields without defaults following fields
# with defaults. This actually would be caught when exec-ing the
# function source code, but catching it here gives a better error
# message, and future-proofs us in case we build up the function
# using ast.
seen_default = False
for f in fields:
# Only consider fields in the __init__ call.
if f.init:
if not (f.default is MISSING and f.default_factory is MISSING):
seen_default = True
elif seen_default:
raise TypeError(f'non-default argument {f.name!r} '
'follows default argument')

locals = {f'_type_{f.name}': f.type for f in fields}
locals.update({
'MISSING': MISSING,
'_HAS_DEFAULT_FACTORY': _HAS_DEFAULT_FACTORY,
})

body_lines = []
for f in fields:
line = _field_init(f, frozen, locals, self_name)
# line is None means that this field doesn't require
# initialization (it's a pseudo-field). Just skip it.
if line:
body_lines.append(line)

# Does this class have a post-init function?
if has_post_init:
params_str = ','.join(f.name for f in fields
if f._field_type is _FIELD_INITVAR)
body_lines.append(f'{self_name}.{_POST_INIT_NAME}({params_str}{", " if params_str else ""} **kwargs)')

# If no body lines, use 'pass'.
if not body_lines:
body_lines = ['pass']

return _create_fn('__init__',
[self_name] + [_init_param(f) for f in fields if f.init] + ["**kwargs"],
body_lines,
locals=locals,
globals=globals,
return_type=None)
else:
from dataclasses import MISSING, _HAS_DEFAULT_FACTORY, _POST_INIT_NAME, _FIELD_INITVAR, _init_param, _field_init, \
_create_fn


def dataclasses_init_fn_with_kwargs(fields, frozen, has_post_init, self_name):
# fields contains both real fields and InitVar pseudo-fields.

# Make sure we don't have fields without defaults following fields
# with defaults. This actually would be caught when exec-ing the
# function source code, but catching it here gives a better error
# message, and future-proofs us in case we build up the function
# using ast.
seen_default = False
for f in fields:
# Only consider fields in the __init__ call.
if f.init:
if not (f.default is MISSING and f.default_factory is MISSING):
seen_default = True
elif seen_default:
raise TypeError(f'non-default argument {f.name!r} '
'follows default argument')

globals = {'MISSING': MISSING,
'_HAS_DEFAULT_FACTORY': _HAS_DEFAULT_FACTORY}

body_lines = []
for f in fields:
line = _field_init(f, frozen, globals, self_name)
# line is None means that this field doesn't require
# initialization (it's a pseudo-field). Just skip it.
if line:
body_lines.append(line)

# Does this class have a post-init function?
if has_post_init:
params_str = ','.join(f.name for f in fields
if f._field_type is _FIELD_INITVAR)
body_lines.append(f'{self_name}.{_POST_INIT_NAME}({params_str}{", " if params_str else ""} **kwargs)')

# If no body lines, use 'pass'.
if not body_lines:
body_lines = ['pass']

locals = {f'_type_{f.name}': f.type for f in fields}
return _create_fn('__init__',
[self_name] + [_init_param(f) for f in fields if f.init] + ["**kwargs"],
body_lines,
locals=locals,
globals=globals,
return_type=None)
30 changes: 26 additions & 4 deletions linkml_runtime/utils/yamlutils.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from copy import copy
from typing import Union, Any, List, Optional, Type, Callable
from json import JSONDecoder
from typing import Union, Any, List, Optional, Type, Callable, Dict

import yaml
from jsonasobj import JsonObj, as_json, ExtendedNamespace
from jsonasobj import JsonObj, as_json, ExtendedNamespace, as_dict, JsonObjTypes, JsonTypes, items
from rdflib import Graph
from yaml.constructor import ConstructorError

from linkml_runtime.utils.context_utils import CONTEXTS_PARAM_TYPE, merge_contexts

YAMLObjTypes = Union[JsonObjTypes, "YAMLRoot"]

class YAMLMark(yaml.error.Mark):
def __str__(self):
Expand Down Expand Up @@ -43,7 +45,7 @@ def _default(self, obj, filtr: Callable[[dict], dict] = None):
:return: Serialized version of obj
"""

if isinstance(obj, JsonObj):
if isinstance(obj, YAMLRoot):
rval = dict()
for k, v in (filtr(obj.__dict__) if filtr else obj.__dict__).items():
is_classvar = k.startswith("type_") and hasattr(type(obj), k)
Expand Down Expand Up @@ -71,7 +73,7 @@ def _default(self, obj, filtr: Callable[[dict], dict] = None):
rval[k] = v
return rval
else:
return super()._default(obj)
return obj._default(obj) if hasattr(obj, '_default') and callable(obj._default) else JSONDecoder().decode(obj)

def _normalize_inlined_slot(self, slot_name: str, slot_type: Type, key_name: Optional[str],
inlined_as_list: Optional[bool], keyed: bool) -> None:
Expand All @@ -87,6 +89,8 @@ def _normalize_inlined_slot(self, slot_name: str, slot_type: Type, key_name: Opt
@param keyed: True means each identifier must be unique
"""
raw_slot: Union[list, dict] = self[slot_name]
if isinstance(raw_slot, JsonObj):
raw_slot = as_dict(raw_slot)
cooked_slot = list() if inlined_as_list else dict()
key_list = list()

Expand Down Expand Up @@ -146,6 +150,24 @@ def cook_a_slot(entry) -> None:
raise ValueError(f"Slot: {slot_name} must be a dictionary or a list")
self[slot_name] = cooked_slot

@staticmethod
def _as_list(value: List[JsonObjTypes]) -> List[JsonTypes]:
""" Return a json array as a list
:param value: array
:return: array with JsonObj instances removed
"""
return [e._as_dict if isinstance(e, YAMLRoot) else e for e in value]

@property
def _as_dict(self) -> Dict[str, JsonTypes]:
""" Convert a JsonObj into a straight dictionary
:return: dictionary that cooresponds to the json object
"""
return {k: v._as_dict if isinstance(v, YAMLRoot) else self._as_list(v) if isinstance(v, list) else v
for k, v in items(self)}


def root_representer(dumper: yaml.Dumper, data: YAMLRoot):
""" YAML callback -- used to filter out empty values (None, {}, [] and false)
Expand Down
8 changes: 4 additions & 4 deletions tests/support/mismatchlog.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ class MismatchLog:
difference_text - the details on the difference. Used for RDF and other non-ascii files
"""
class MismatchLogEntry:
@dataclass
class StackFrame:
filename: str
method: str
line: int
def __init__(self, filename: str, method: str, line: int) -> None:
self.filename = filename
self.method = method
self.line = line

def __str__(self):
return f'File "{self.filename}", line {self.line} in {self.method} '
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ envlist = py37, py38, py39
setenv = PIPENV_SKIP_LOCK=1
PIPENV_DEV=1
PIPENV_IGNORE_VIRTUALENVS=1
PIPENV_VERBOSITY=-1

[testenv]
whitelist_externals = python
Expand Down

0 comments on commit 99f0e23

Please sign in to comment.