Skip to content

Commit 6ec6639

Browse files
committed
Merge branch 'develop' into arraylike
2 parents e748e4d + 2812594 commit 6ec6639

11 files changed

Lines changed: 123 additions & 60 deletions

File tree

.github/codeql.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
paths:
2+
- "jmespath/"

.github/dependabot.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "github-actions"
4+
directory: "/"
5+
schedule:
6+
interval: "weekly"
7+
ignore:
8+
- dependency-name: "*"
9+
update-types: ["version-update:semver-patch"]

.github/workflows/codeql.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: "CodeQL"
2+
3+
on:
4+
push:
5+
branches: ["develop"]
6+
pull_request:
7+
branches: ["develop"]
8+
schedule:
9+
- cron: "0 0 * * 5"
10+
11+
permissions:
12+
contents: "read"
13+
14+
jobs:
15+
analyze:
16+
name: "Analyze"
17+
runs-on: "ubuntu-latest"
18+
permissions:
19+
actions: read
20+
contents: read
21+
security-events: write
22+
steps:
23+
- name: "Checkout repository"
24+
uses: "actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3"
25+
26+
- name: "Run CodeQL init"
27+
uses: "github/codeql-action/init@4e94bd11f71e507f7f87df81788dff88d1dacbfb"
28+
with:
29+
config-file: "./.github/codeql.yml"
30+
languages: "python"
31+
32+
- name: "Run CodeQL autobuild"
33+
uses: "github/codeql-action/autobuild@4e94bd11f71e507f7f87df81788dff88d1dacbfb"
34+
35+
- name: "Run CodeQL analyze"
36+
uses: "github/codeql-action/analyze@4e94bd11f71e507f7f87df81788dff88d1dacbfb"

.github/workflows/run-tests.yml

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,31 @@ name: Run Tests
22

33
on: [push, pull_request]
44

5+
permissions:
6+
contents: read
7+
58
jobs:
69
build:
710

811
runs-on: ${{ matrix.os }}
912
strategy:
13+
fail-fast: false
1014
matrix:
11-
os: [ubuntu-latest]
12-
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11-dev"]
15+
os: [ubuntu-latest, macOS-latest, windows-latest]
16+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
1317

1418
steps:
15-
- uses: actions/checkout@v2
19+
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3
1620
- name: Set up Python ${{ matrix.python-version }}
17-
uses: actions/setup-python@v2
21+
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548
1822
with:
1923
python-version: ${{ matrix.python-version }}
2024
- name: Install dependencies
2125
run: |
22-
pip install pip==20.0.2
26+
pip install pip==25.1.0
2327
pip install -r requirements.txt
24-
python setup.py bdist_wheel
25-
pip install dist/*.whl
28+
python -m pip install .
2629
- name: Test with pytest
2730
run: |
28-
cd tests/ && py.test --cov jmespath --cov-report term-missing
31+
cd tests/ && python -m pytest --cov jmespath --cov-report term-missing
32+

CHANGELOG.rst

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1-
Unreleased
2-
==========
1+
Next Release (TBD)
2+
==================
33

44
* Added support for Arraylike python objects as json arrays.
55

6+
7+
1.1.0
8+
=====
9+
10+
* Fix concurrency issue with cache
11+
(`pr #335 <https://github.com/jmespath/jmespath.py/pull/335>`__)
12+
* Added support for Python 3.12-3.14 (`pr #331 <https://github.com/jmespath/jmespath.py/pull/331>`__)
13+
* Removed support for Python 3.7-3.8 (`pr #335 <https://github.com/jmespath/jmespath.py/pull/335>`__)
14+
15+
616
1.0.1
717
=====
818

jmespath/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from jmespath import parser
22
from jmespath.visitor import Options
33

4-
__version__ = '1.0.1'
4+
__version__ = '1.1.0'
55

66

77
def compile(expression):

jmespath/parser.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@
2525
consuming from the token iterator one token at a time.
2626
2727
"""
28-
import random
29-
3028
from jmespath import lexer
3129
from jmespath.compat import with_repr_method
3230
from jmespath import ast
@@ -73,7 +71,7 @@ class Parser(object):
7371
# The _MAX_SIZE most recent expressions are cached in
7472
# _CACHE dict.
7573
_CACHE = {}
76-
_MAX_SIZE = 128
74+
_MAX_SIZE = 512
7775

7876
def __init__(self, lookahead=2):
7977
self.tokenizer = None
@@ -82,13 +80,26 @@ def __init__(self, lookahead=2):
8280
self._index = 0
8381

8482
def parse(self, expression):
85-
cached = self._CACHE.get(expression)
86-
if cached is not None:
87-
return cached
83+
try:
84+
return self._CACHE[expression]
85+
except KeyError:
86+
pass
8887
parsed_result = self._do_parse(expression)
88+
if len(self._CACHE) >= self._MAX_SIZE:
89+
try:
90+
del self._CACHE[next(iter(self._CACHE))]
91+
except (KeyError, StopIteration, RuntimeError):
92+
# KeyError - Another thread else already deleted the key.
93+
# RuntimeError - Another modified the cache.
94+
# StopIteration - (Unlikely) Cache is empty.
95+
#
96+
# If we encounter an error we should NOT be adding to the
97+
# cache. To ensure we do not exceed self._MAX_SIZE, we
98+
# can only add to the cache if we successfully removed
99+
# an element from the cache, otherwise this can grow
100+
# unbounded.
101+
return parsed_result
89102
self._CACHE[expression] = parsed_result
90-
if len(self._CACHE) > self._MAX_SIZE:
91-
self._free_cache_entries()
92103
return parsed_result
93104

94105
def _do_parse(self, expression):
@@ -488,10 +499,6 @@ def _raise_parse_error_maybe_eof(self, expected_type, token):
488499
raise exceptions.ParseError(
489500
lex_position, actual_value, actual_type, message)
490501

491-
def _free_cache_entries(self):
492-
for key in random.sample(list(self._CACHE.keys()), int(self._MAX_SIZE / 2)):
493-
self._CACHE.pop(key, None)
494-
495502
@classmethod
496503
def purge(cls):
497504
"""Clear the expression compilation cache."""

requirements.txt

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
wheel==0.38.1
2-
parameterized==0.9.0
3-
pytest==6.2.5
1+
wheel==0.45.1
2+
pytest==8.4.1
43
pytest-cov==3.0.0
5-
hypothesis==3.1.0 ; python_version < '3.8'
6-
hypothesis==5.5.4 ; python_version == '3.8'
7-
hypothesis==5.35.4 ; python_version == '3.9'
4+
parameterized==0.9.0
5+
hypothesis==5.35.4
6+
7+
# Setuptools is no longer provided by default in Python 3.12+
8+
setuptools==71.1.0 ; python_version >= '3.12'
9+
packaging==24.1 ; python_version >= '3.12'
10+
811
astropy>=3.1
912
dask>=2.0.0
1013
numpy>=1.15.0
11-
xarray>=0.18.0
14+
xarray>=0.18.0

setup.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
setup(
99
name='jmespath',
10-
version='1.0.1',
10+
version='1.1.0',
1111
description='JSON Matching Expressions',
1212
long_description=io.open('README.rst', encoding='utf-8').read(),
1313
author='James Saryerwinnie',
@@ -16,19 +16,20 @@
1616
scripts=['bin/jp.py'],
1717
packages=find_packages(exclude=['tests']),
1818
license='MIT',
19-
python_requires='>=3.7',
19+
python_requires='>=3.9',
2020
classifiers=[
2121
'Development Status :: 5 - Production/Stable',
2222
'Intended Audience :: Developers',
2323
'Natural Language :: English',
2424
'License :: OSI Approved :: MIT License',
2525
'Programming Language :: Python',
2626
'Programming Language :: Python :: 3',
27-
'Programming Language :: Python :: 3.7',
28-
'Programming Language :: Python :: 3.8',
2927
'Programming Language :: Python :: 3.9',
3028
'Programming Language :: Python :: 3.10',
3129
'Programming Language :: Python :: 3.11',
30+
'Programming Language :: Python :: 3.12',
31+
'Programming Language :: Python :: 3.13',
32+
'Programming Language :: Python :: 3.14',
3233
'Programming Language :: Python :: Implementation :: CPython',
3334
'Programming Language :: Python :: Implementation :: PyPy',
3435
],

tests/test_compliance.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,20 @@ def _walk_files():
4848

4949

5050
def load_cases(full_path):
51-
all_test_data = json.load(open(full_path), object_pairs_hook=OrderedDict)
52-
for test_data in all_test_data:
53-
given = test_data['given']
54-
for case in test_data['cases']:
55-
if 'result' in case:
56-
test_type = 'result'
57-
elif 'error' in case:
58-
test_type = 'error'
59-
elif 'bench' in case:
60-
test_type = 'bench'
61-
else:
62-
raise RuntimeError("Unknown test type: %s" % json.dumps(case))
63-
yield (given, test_type, case)
51+
with open(full_path, 'r', encoding='utf-8') as f:
52+
all_test_data = json.load(f, object_pairs_hook=OrderedDict)
53+
for test_data in all_test_data:
54+
given = test_data['given']
55+
for case in test_data['cases']:
56+
if 'result' in case:
57+
test_type = 'result'
58+
elif 'error' in case:
59+
test_type = 'error'
60+
elif 'bench' in case:
61+
test_type = 'bench'
62+
else:
63+
raise RuntimeError(f"Unknown test type: {json.dumps(case)}")
64+
yield (given, test_type, case)
6465

6566

6667
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)