Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

# Version 0.3.62

* Migrate to pyparsing 3.x and drop Python 2.7 support (@alejandrorm) [#337]

# Version 0.3.61

* fix(tox): remove old EOLed python 3.x versions, added new python versions (@pierresouchay) [#330]
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ assert config == d
- Carol Guo ([@carolguo-dd](https://github.com/carolguo-dd))
- Jakub Kubík ([@M0dEx](https://github.com/M0dEx))
- Jakub Szewczyk ([@jakub-szewczyk-exa](https://github.com/jakub-szewczyk-exa))
- Alejandro Rodriguez-Morantes ([@alejandrorm](https://github.com/alejandrorm))

### Thanks

Expand All @@ -408,4 +409,4 @@ assert config == d
- Dominik1123 ([@Dominik1123](https://github.com/Dominik1123))
- Richard Taylor ([@richard534](https://github.com/richard534))
- Sergii Lutsanych ([@sergii1989](https://github.com/sergii1989))

- Romain G ([@Romain-DE](https://github.com/Romain-DE))
36 changes: 19 additions & 17 deletions pyhocon/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def fixed_get_attr(self, item):
except KeyError:
return ""


pyparsing.ParseResults.__getattr__ = fixed_get_attr

from pyhocon.config_tree import (ConfigInclude, ConfigList, ConfigQuotedString,
Expand All @@ -50,6 +49,7 @@ def find_package_dirs(name):
raise ImportError('No module named {!r}'.format(name))
return spec.submodule_search_locations


logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -350,7 +350,7 @@ def set_default_white_spaces():
false_expr = Keyword("false", case_insensitive=True).set_parse_action(replace_with(False))
null_expr = Keyword("null", case_insensitive=True).set_parse_action(replace_with(NoneValue()))
key = QuotedString('"""', esc_char='\\', unquote_results=False) | \
QuotedString('"', esc_char='\\', unquote_results=False) | Word(alphanums + alphas8bit + '._- /')
QuotedString('"', esc_char='\\', unquote_results=False) | Word(alphanums + alphas8bit + '._- /')

eol = Word('\n\r').suppress()
eol_comma = Word('\n\r,').suppress()
Expand All @@ -377,15 +377,16 @@ def set_default_white_spaces():
value_expr = get_period_expr() | number_expr | true_expr | false_expr | null_expr | string_expr

include_content = (
quoted_string | ((Keyword('url') | Keyword('file') | Keyword('package')) - Literal(
'(').suppress() - quoted_string - Literal(')').suppress())
quoted_string | ((Keyword('url') | Keyword('file') | Keyword('package'))
- Literal('(').suppress() - quoted_string
- Literal(')').suppress())
)
include_expr = (
Keyword("include", case_insensitive=True).suppress() + (
Keyword("include", case_insensitive=True).suppress() + (
include_content | (
Keyword("required") - Literal('(').suppress() - include_content - Literal(')').suppress()
)
)
Keyword("required") - Literal('(').suppress() - include_content - Literal(')').suppress()
)
)
).set_parse_action(include_config)

root_dict_expr = Forward()
Expand All @@ -407,19 +408,20 @@ def set_default_white_spaces():
# special case when we have a value assignment where the string can potentially be the remainder of the line
assign_expr << Group(
key - ZeroOrMore(comment_no_comma_eol) - (
dict_expr | (Literal('=') | Literal(':') | Literal('+=')) - ZeroOrMore(
comment_no_comma_eol) - ConcatenatedValueParser(multi_value_expr))
dict_expr | (Literal('=') | Literal(':') | Literal('+=')) - ZeroOrMore(
comment_no_comma_eol) - ConcatenatedValueParser(multi_value_expr))
)

# the file can be { ... } where {} can be omitted or []
config_expr = ZeroOrMore(comment_eol | eol) + (
list_expr | root_dict_expr | inside_root_dict_expr) + ZeroOrMore(
list_expr | root_dict_expr | inside_root_dict_expr
) + ZeroOrMore(
comment_eol | eol_comma)
config = config_expr.parse_string(content, parse_all=True)[0]

if resolve:
allow_unresolved = resolve and unresolved_value is not DEFAULT_SUBSTITUTION \
and unresolved_value is not MANDATORY_SUBSTITUTION
allow_unresolved = resolve and unresolved_value is not DEFAULT_SUBSTITUTION
allow_unresolved = allow_unresolved and unresolved_value is not MANDATORY_SUBSTITUTION
has_unresolved = cls.resolve_substitutions(config, allow_unresolved)
if has_unresolved and unresolved_value is MANDATORY_SUBSTITUTION:
raise ConfigSubstitutionException(
Expand Down Expand Up @@ -535,8 +537,8 @@ def _do_substitute(cls, substitution, resolved_value, is_optional_resolved=True)
# if it is a string, then add the extra ws that was present in the original string after the substitution
formatted_resolved_value = resolved_value \
if resolved_value is None \
or isinstance(resolved_value, (dict, list)) \
or substitution.index == len(config_values.tokens) - 1 \
or isinstance(resolved_value, (dict, list)) \
or substitution.index == len(config_values.tokens) - 1 \
else (str(resolved_value) + substitution.ws)
# use a deepcopy of resolved_value to avoid mutation
config_values.put(substitution.index, copy.deepcopy(formatted_resolved_value))
Expand Down Expand Up @@ -606,11 +608,11 @@ def resolve_substitutions(cls, config, accept_unresolved=False):

is_optional_resolved, resolved_value = cls._resolve_variable(config, substitution)

if isinstance(resolved_value, ConfigValues) :
if isinstance(resolved_value, ConfigValues):
resolved_value = resolved_value.transform()
value_to_be_substitute = resolved_value
if overridden_value and not isinstance(overridden_value, ConfigValues):
value_to_be_substitute = overridden_value
value_to_be_substitute = overridden_value
unresolved, _, _ = cls._do_substitute(substitution, value_to_be_substitute, is_optional_resolved)

any_unresolved = unresolved or any_unresolved
Expand Down
1 change: 0 additions & 1 deletion pyhocon/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,4 +284,3 @@ def _escape_match(cls, match):
@classmethod
def _escape_string(cls, string):
return re.sub(r'[\x00-\x1F"\\]', cls._escape_match, string)

4 changes: 2 additions & 2 deletions pyhocon/period_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ def get_period_expr():
# Allow only spaces as a valid separator between value and unit.
# E.g. \t as a separator is invalid: '10<TAB>weeks'.
return Combine(
Word(nums)('value') + ZeroOrMore(Literal(" ")).suppress() + Or(period_types)('unit') + WordEnd(
alphanums).suppress()
Word(nums)('value') + ZeroOrMore(Literal(" ")).suppress() + Or(period_types)('unit') + WordEnd(
alphanums).suppress()
).set_parse_action(convert_period)


Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[flake8]
ignore =
ignore = W503
max-line-length = 160
statistics = True
count = True
Expand Down
29 changes: 1 addition & 28 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,10 @@
#!/usr/bin/env python

import sys

from setuptools import setup
from setuptools.command.test import test as TestCommand


class PyTestCommand(TestCommand):
user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]

def initialize_options(self):
TestCommand.initialize_options(self)
self.pytest_args = []

def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True

def run_tests(self):
import pytest
errno = pytest.main(self.pytest_args)
sys.exit(errno)


setup(
name='pyhocon',
version='0.3.61',
version='0.3.62',
description='HOCON parser for Python',
long_description='pyhocon is a HOCON parser for Python. Additionally we provide a tool (pyhocon) to convert any HOCON '
'content into json, yaml and properties format.',
Expand Down Expand Up @@ -56,14 +34,9 @@ def run_tests(self):
extras_require={
'Duration': ['python-dateutil>=2.8.0']
},
tests_require=['pytest', 'mock==3.0.5'],
entry_points={
'console_scripts': [
'pyhocon=pyhocon.tool:main'
]
},
test_suite='tests',
cmdclass={
'test': PyTestCommand
}
)
15 changes: 6 additions & 9 deletions tests/test_config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
except Exception:
from datetime import timedelta as period


class TestConfigParser(object):
def test_parse_simple_value(self):
config = ConfigFactory.parse_string(
Expand Down Expand Up @@ -118,7 +119,6 @@ def test_parse_with_enclosing_brace_and_period_like_value(self):
assert config.get_string('a.b') == '5'
assert config.get_string('a.y_min') == '42'


def test_issue_324(self):
config = ConfigFactory.parse_string("a { c = 3\nd = 4 }")
assert config["a"]["c"] == 3
Expand Down Expand Up @@ -194,11 +194,10 @@ def test_parse_with_list_mixed_types_with_durations_and_trailing_comma(self):
# Depending if parsing dates is enabled, might parse date or might not
# since this is an optional dependency
assert (
config['b'] == ['a', 1, period(weeks=10), period(minutes=5)]
) or (
config['b'] == ['a', 1, '10 weeks', '5 minutes']
)

config['b'] == ['a', 1, period(weeks=10), period(minutes=5)]
) or (
config['b'] == ['a', 1, '10 weeks', '5 minutes']
)

def test_parse_with_enclosing_square_bracket(self):
config = ConfigFactory.parse_string("[1, 2, 3]")
Expand Down Expand Up @@ -1757,12 +1756,11 @@ def test_override_optional_substitution(self):
result = ${test}
""")
assert config == {
'a' : 3,
'a': 3,
'test': 3,
'result': 3
}


def test_substitution_cycle(self):
with pytest.raises(ConfigSubstitutionException):
ConfigFactory.parse_string(
Expand Down Expand Up @@ -2701,7 +2699,6 @@ def test_triple_quotes_keys_triple_quotes_values_second_separator(self):
try:
from dateutil.relativedelta import relativedelta


@pytest.mark.parametrize('data_set', [
('a: 1 months', relativedelta(months=1)),
('a: 1months', relativedelta(months=1)),
Expand Down
2 changes: 0 additions & 2 deletions tests/test_periods.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ def test_parse_string_with_duration(data_set):
try:
from dateutil.relativedelta import relativedelta


@pytest.mark.parametrize('data_set', [
('1 months', relativedelta(months=1)),
('1months', relativedelta(months=1)),
Expand All @@ -82,7 +81,6 @@ def test_parse_string_with_duration_optional_units(data_set):

assert parsed == data_set[1]


def test_format_relativedelta():

for time_delta, expected_result in ((relativedelta(seconds=0), '0 seconds'),
Expand Down
8 changes: 5 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@
envlist = flake8, py{37,38,39,310,311,312,313}

[testenv]
passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH
passenv = TRAVIS,TRAVIS_JOB_ID,TRAVIS_BRANCH
deps =
pytest
coveralls
setuptools
mock
python-dateutil>=2.8.0
PyYAML
# for python 3.4
typing
commands =
coverage run --source=pyhocon setup.py test
coverage run --source=pyhocon -m pytest tests
coverage report -m
coveralls

[testenv:flake8]
basepython = python
Expand Down