Skip to content

Commit

Permalink
chore: Some arguments must be keyword arguments. Run ruff check .
Browse files Browse the repository at this point in the history
  • Loading branch information
jpmckinney committed Sep 14, 2024
1 parent 629ab6c commit 1679ff7
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 55 deletions.
14 changes: 13 additions & 1 deletion docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
Changelog
=========

0.3.0 (Unreleased)
------------------

Changed
~~~~~~~

Some arguments must be keyword arguments:

- :meth:`jscc.testing.checks.validate_null_type`
- :meth:`jscc.testing.checks.validate_schema_codelists_match`
- :meth:`jscc.testing.checks.test_validate_codelist_enum`

0.2.4 (2024-01-29)
------------------

Expand All @@ -19,7 +31,7 @@ Changed

- :meth:`jscc.testing.checks.validate_letter_case`
- :meth:`jscc.testing.checks.validate_metadata_presence`
- :meth:`jscc.testing.checks.validate_null_types`
- :meth:`jscc.testing.checks.validate_null_type`
- :meth:`jscc.testing.checks.validate_deep_properties`
- :meth:`jscc.schema.is_json_schema`
- :meth:`jscc.schema.is_json_merge_patch`
Expand Down
5 changes: 1 addition & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))

import os
import sys

Expand Down
5 changes: 5 additions & 0 deletions jscc/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ def extend_schema(basename, schema, metadata, codelists=None):
If :code:`codelists` is provided, it will be updated with the codelists from the dependencies.
.. attention::
No timeout is set. If a user can input malicious ``metadata`` with unresponsive ``dependencies`` or
``testDependencies`` URLs, the program can hang indefinitely.
:param str basename: the JSON Schema file's basename
:param dict schema: the JSON Schema file's parsed contents
:param dict metadata: the extension metadata file's parsed contents
Expand Down
44 changes: 15 additions & 29 deletions jscc/testing/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,12 @@ def test_empty():
continue # the file is non-empty, and might be binary

if not text.strip():
yield path,
yield (path,)
elif name.endswith('.json'):
try:
value = json.loads(text)
if not value and not isinstance(value, (bool, int, float)):
yield path,
yield (path,)
except json.decoder.JSONDecodeError:
continue # the file is non-empty

Expand Down Expand Up @@ -189,7 +189,7 @@ def test_indent():
if tracked(path) and include(path, name):
expected = json.dumps(data, ensure_ascii=False, indent=2) + '\n'
if text != expected:
yield path,
yield (path,)


def get_invalid_json_files(**kwargs):
Expand All @@ -209,7 +209,7 @@ def test_invalid_json():
warn_and_assert(get_invalid_json_files(), '{0} is not valid JSON: {1}',
'JSON files are invalid. See warnings below.')
"""
for path, name in walk(**kwargs):
for path, _ in walk(**kwargs):
if path.endswith('.json'):
with open(path) as f:
text = f.read()
Expand Down Expand Up @@ -259,12 +259,12 @@ def block(path, data, pointer):
parent = pointer.rsplit('/', 1)[-1]

if parent == 'properties':
for key in data.keys():
for key in data:
if not re.search(r'^[a-z][A-Za-z]+$', key) and key not in property_exceptions:
errors += 1
warn(f"{path}: {pointer}/{key} field isn't lowerCamelCase ASCII letters", LetterCaseWarning)
elif parent in ('definitions', '$defs'):
for key in data.keys():
for key in data:
if not re.search(r'^[A-Z][A-Za-z]+$', key) and key not in definition_exceptions:
errors += 1
warn(f"{path}: {pointer}/{key} block isn't UpperCamelCase ASCII letters", LetterCaseWarning)
Expand All @@ -286,7 +286,7 @@ def validate_metadata_presence(*args, allow_missing=_false):
not have a "title" or "description" property
:returns: the number of errors
:rtype: int
""" # noqa: E501
"""
schema_fields = {'definitions', '$defs', 'deprecated', 'items', 'patternProperties', 'properties'}
schema_sections = {'patternProperties'}
required_properties = {'title', 'description'}
Expand All @@ -295,10 +295,7 @@ def block(path, data, pointer):
errors = 0

parts = pointer.rsplit('/')
if len(parts) >= 3:
grandparent = parts[-2]
else:
grandparent = None
grandparent = parts[-2] if len(parts) >= 3 else None # noqa: PLR2004
parent = parts[-1]

# Look for metadata fields on user-defined objects only. (Add exceptional condition for "items" field.)
Expand All @@ -318,7 +315,7 @@ def block(path, data, pointer):
return _traverse(block)(*args)


def validate_null_type(path, data, pointer='', no_null=False, expect_null=True, allow_object_null=(),
def validate_null_type(path, data, pointer='', *, no_null=False, expect_null=True, allow_object_null=(),
allow_no_null=(), allow_null=()):
"""
Warns and returns the number of errors relating to non-nullable optional fields and nullable required fields.
Expand Down Expand Up @@ -419,10 +416,8 @@ def block(path, data, pointer):
parent = pointer.rsplit('/', 1)[-1]

if 'codelist' in data:
if 'type' not in data: # e.g. if changing an existing property
types = fallback.get(pointer, ['array']) # otherwise, assume "array"
else:
types = get_types(data)
# `type` can be missing if changing an existing property.
types = get_types(data) if 'type' in data else fallback.get(pointer, ['array'])

if data['openCodelist']:
if ('string' in types and 'enum' in data or 'array' in types and 'enum' in data['items']):
Expand Down Expand Up @@ -550,10 +545,7 @@ def block(path, data, pointer):
errors = 0

parts = pointer.rsplit('/', 2)
if len(parts) == 3:
grandparent = parts[-2]
else:
grandparent = None
grandparent = parts[-2] if len(parts) == 3 else None # noqa: PLR2004

if (
pointer
Expand Down Expand Up @@ -593,10 +585,7 @@ def block(path, data, pointer):
if 'type' in data and 'array' in data['type'] and 'properties' in data.get('items', {}):
required = data['items'].get('required', [])

if hasattr(data['items'], '__reference__'):
original = data['items'].__reference__['$ref'][1:]
else:
original = pointer
original = data['items'].__reference__['$ref'][1:] if hasattr(data['items'], '__reference__') else pointer

# See https://standard.open-contracting.org/latest/en/schema/merging/#whole-list-merge
if 'id' not in data['items']['properties']:
Expand Down Expand Up @@ -673,7 +662,7 @@ def validate_ref(path, data, **kwargs):
return 0


def validate_schema_codelists_match(path, data, top, is_extension=False, is_profile=False, external_codelists=None):
def validate_schema_codelists_match(path, data, top, *, is_extension=False, is_profile=False, external_codelists=None):
"""
Warns and returns the number of errors relating to mismatches between codelist files and codelist references from
JSON Schema.
Expand Down Expand Up @@ -726,10 +715,7 @@ def collect_codelist_values(path, data, pointer=''):
codelist_files.add(csvname)

codelist_values = collect_codelist_values(path, data)
if is_extension:
all_codelist_files = codelist_files | external_codelists
else:
all_codelist_files = codelist_files
all_codelist_files = codelist_files | external_codelists if is_extension else codelist_files

unused_codelists = [codelist for codelist in codelist_files if codelist not in codelist_values]
missing_codelists = [codelist for codelist in codelist_values if codelist not in all_codelist_files]
Expand Down
20 changes: 9 additions & 11 deletions jscc/testing/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ def http_get(url):
"""
Sends and caches an HTTP GET request.
.. attention:: No timeout is set. If a user can input a malicious URL, the program can hang indefinitely.
:param str url: the URL to request
"""
response = requests.get(url)
response = requests.get(url) # noqa: S113
response.raise_for_status()
return response

Expand All @@ -25,9 +27,11 @@ def http_head(url):
"""
Sends and caches an HTTP HEAD request.
.. attention:: No timeout is set. If a user can input a malicious URL, the program can hang indefinitely.
:param str url: the URL to request
"""
response = requests.head(url)
response = requests.head(url) # noqa: S113
response.raise_for_status()
return response

Expand All @@ -48,16 +52,10 @@ def difference(actual, expected):
:param set expected: the expected set
"""
added = actual - expected
if added:
added = f'; added {added}'
else:
added = ''
added = f'; added {added}' if added else ''

removed = expected - actual
if removed:
removed = f'; removed {removed}'
else:
removed = ''
removed = f'; removed {removed}' if removed else ''

return added, removed

Expand All @@ -75,4 +73,4 @@ def warn_and_assert(paths, warn_message, assert_message):
warnings.warn('ERROR: ' + warn_message.format(*args))
success = False

assert success, assert_message
assert success, assert_message # noqa: S101
11 changes: 5 additions & 6 deletions tests/test_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,11 @@ def test_get_invalid_json_files():

def test_validate_codelist_enum():
directory = os.path.realpath(path('schema')) + os.sep
with chdir(directory):
with pytest.warns(UserWarning) as records:
filepath = os.path.join(directory, 'codelist_enum.json')
with open(filepath) as f:
data = json.load(f)
errors = validate_codelist_enum(filepath, data)
with chdir(directory), pytest.warns(UserWarning) as records:
filepath = os.path.join(directory, 'codelist_enum.json')
with open(filepath) as f:
data = json.load(f)
errors = validate_codelist_enum(filepath, data)

assert sorted(str(record.message).replace(directory, '') for record in records) == [
'codelist_enum.json is missing "codelist" and "openCodelist" at /properties/noCodelistArray',
Expand Down
2 changes: 1 addition & 1 deletion tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def test_extend_schema():
}
metadata = {
'dependencies': [
'https://raw.githubusercontent.com/open-contracting-extensions/ocds_process_title_extension/v1.1.4/extension.json', # noqa: E501
'https://raw.githubusercontent.com/open-contracting-extensions/ocds_process_title_extension/v1.1.4/extension.json',
]
}
codelists = set()
Expand Down
5 changes: 2 additions & 3 deletions tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ def test_http_get_error():


def test_warn_and_assert():
with pytest.raises(AssertionError) as excinfo:
with pytest.warns(UserWarning) as records:
warn_and_assert([('path/',)], '{0} is invalid', 'See errors above')
with pytest.raises(AssertionError) as excinfo, pytest.warns(UserWarning) as records:
warn_and_assert([('path/',)], '{0} is invalid', 'See errors above')

assert len(records) == 1
assert str(records[0].message) == 'ERROR: path/ is invalid'
Expand Down

0 comments on commit 1679ff7

Please sign in to comment.