Skip to content

Commit

Permalink
bumped version to 0.3.39 and updated README
Browse files Browse the repository at this point in the history
  • Loading branch information
darthbear committed May 2, 2018
1 parent 4ac59c3 commit 9ca1bf4
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 255 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

# Version 0.3.39
* Fixed self ref substitution (@aalba6675) [#142]
* Fixed with_fallback to not mutate config (@aalba6675) [#143]
* Fixed self reference (@aalba6675) [#146]
* Fixed complex substitutions (@aalba6675) [#148]
* Support attribute path a.b.c. (@chunyang-wen) [#150]
* Updated python version (@hugovk) [#151]


# Version 0.3.38
* Added compact option for hocon output. #129
* Unicode fix for unquoted strings. #130
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,9 @@ Java properties mapping | :x:
- jjtk88 ([@jjtk88](https://github.com/jjtk88))
- Aki Ariga ([@chezou](https://github.com/chezou))
- Joel Grus ([@joelgrus](https://github.com/joelgrus))
- aalba6675 [@aalba6675](https://github.com/aalba6675)
- hugovk [@hugovk](https://github.com/hugovk)
- chunyang-wen [@chunyang-wen](https://github.com/chunyang-wen)

### Thanks

Expand Down
2 changes: 1 addition & 1 deletion pyhocon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
from pyhocon.config_tree import ConfigTree, ConfigList, UndefinedKey # noqa
from pyhocon.config_tree import ConfigInclude, ConfigSubstitution, ConfigUnquotedString, ConfigValues # noqa
from pyhocon.config_tree import ConfigMissingException, ConfigException, ConfigWrongTypeException # noqa
from pyhocon.tool import HOCONConverter # noqa
from pyhocon.converter import HOCONConverter # noqa
8 changes: 4 additions & 4 deletions pyhocon/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,10 +452,10 @@ def resolve_substitutions(config):
link = getattr(link, 'overriden_value')
cache[resolved_value] = parents

if (isinstance(resolved_value, ConfigValues) and
substitution.parent in parents and
hasattr(substitution.parent, "overriden_value") and
substitution.parent.overriden_value):
if isinstance(resolved_value, ConfigValues) \
and substitution.parent in parents \
and hasattr(substitution.parent, "overriden_value") \
and substitution.parent.overriden_value:

# self resolution, backtrack
resolved_value = substitution.parent.overriden_value
Expand Down
25 changes: 14 additions & 11 deletions pyhocon/config_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ class UndefinedKey(object):
pass


class NonExistentKey(object):
pass


class NoneValue(object):
pass

Expand Down Expand Up @@ -78,21 +82,21 @@ def _put(self, key_path, value, append=False):
elif append:
# If we have t=1
# and we try to put t.a=5 then t is replaced by {a: 5}
l = self.get(key_elt, None)
if isinstance(l, ConfigValues):
l.tokens.append(value)
l.recompute()
elif isinstance(l, ConfigTree) and isinstance(value, ConfigValues):
value.overriden_value = l
value.tokens.insert(0,l)
l_value = self.get(key_elt, None)
if isinstance(l_value, ConfigValues):
l_value.tokens.append(value)
l_value.recompute()
elif isinstance(l_value, ConfigTree) and isinstance(value, ConfigValues):
value.overriden_value = l_value
value.tokens.insert(0, l_value)
value.recompute()
value.parent = self
value.key = key_elt
self._push_history(key_elt, value)
self[key_elt] = value
elif isinstance(l_value, list) and isinstance(value, ConfigValues):
self._push_history(key_elt, value)
value.overriden_value = l
value.overriden_value = l_value
value.parent = self
value.key = key_elt
self[key_elt] = value
Expand Down Expand Up @@ -341,9 +345,8 @@ def __getitem__(self, item):
return val

def __getattr__(self, item):
class NotExsitedKey(object): pass
val = self.get(item, NotExsitedKey)
if val is NotExsitedKey:
val = self.get(item, NonExistentKey)
if val is NonExistentKey:
return super(ConfigTree, self).__getattr__(item)
return val

Expand Down
240 changes: 240 additions & 0 deletions pyhocon/converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import sys

from pyhocon import ConfigFactory
from pyhocon.config_tree import ConfigTree
from pyhocon.config_tree import NoneValue

try:
basestring
except NameError:
basestring = str


class HOCONConverter(object):
@classmethod
def to_json(cls, config, compact=False, indent=2, level=0):
"""Convert HOCON input into a JSON output
:return: JSON string representation
:type return: basestring
"""
lines = ""
if isinstance(config, ConfigTree):
if len(config) == 0:
lines += '{}'
else:
lines += '{\n'
bet_lines = []
for key, item in config.items():
bet_lines.append('{indent}"{key}": {value}'.format(
indent=''.rjust((level + 1) * indent, ' '),
key=key.strip('"'), # for dotted keys enclosed with "" to not be interpreted as nested key
value=cls.to_json(item, compact, indent, level + 1))
)
lines += ',\n'.join(bet_lines)
lines += '\n{indent}}}'.format(indent=''.rjust(level * indent, ' '))
elif isinstance(config, list):
if len(config) == 0:
lines += '[]'
else:
lines += '[\n'
bet_lines = []
for item in config:
bet_lines.append('{indent}{value}'.format(
indent=''.rjust((level + 1) * indent, ' '),
value=cls.to_json(item, compact, indent, level + 1))
)
lines += ',\n'.join(bet_lines)
lines += '\n{indent}]'.format(indent=''.rjust(level * indent, ' '))
elif isinstance(config, basestring):
lines = '"{value}"'.format(value=config.replace('\n', '\\n').replace('"', '\\"'))
elif config is None or isinstance(config, NoneValue):
lines = 'null'
elif config is True:
lines = 'true'
elif config is False:
lines = 'false'
else:
lines = str(config)
return lines

@classmethod
def to_hocon(cls, config, compact=False, indent=2, level=0):
"""Convert HOCON input into a HOCON output
:return: JSON string representation
:type return: basestring
"""
lines = ""
if isinstance(config, ConfigTree):
if len(config) == 0:
lines += '{}'
else:
if level > 0: # don't display { at root level
lines += '{\n'
bet_lines = []

for key, item in config.items():
if compact:
full_key = key
while isinstance(item, ConfigTree) and len(item) == 1:
key, item = next(iter(item.items()))
full_key += '.' + key
else:
full_key = key

bet_lines.append('{indent}{key}{assign_sign} {value}'.format(
indent=''.rjust(level * indent, ' '),
key=full_key,
assign_sign='' if isinstance(item, dict) else ' =',
value=cls.to_hocon(item, compact, indent, level + 1))
)
lines += '\n'.join(bet_lines)

if level > 0: # don't display { at root level
lines += '\n{indent}}}'.format(indent=''.rjust((level - 1) * indent, ' '))
elif isinstance(config, list):
if len(config) == 0:
lines += '[]'
else:
lines += '[\n'
bet_lines = []
for item in config:
bet_lines.append('{indent}{value}'.format(indent=''.rjust(level * indent, ' '),
value=cls.to_hocon(item, compact, indent, level + 1)))
lines += '\n'.join(bet_lines)
lines += '\n{indent}]'.format(indent=''.rjust((level - 1) * indent, ' '))
elif isinstance(config, basestring):
if '\n' in config:
lines = '"""{value}"""'.format(value=config) # multilines
else:
lines = '"{value}"'.format(value=config.replace('\n', '\\n').replace('"', '\\"'))
elif config is None or isinstance(config, NoneValue):
lines = 'null'
elif config is True:
lines = 'true'
elif config is False:
lines = 'false'
else:
lines = str(config)
return lines

@classmethod
def to_yaml(cls, config, compact=False, indent=2, level=0):
"""Convert HOCON input into a YAML output
:return: YAML string representation
:type return: basestring
"""
lines = ""
if isinstance(config, ConfigTree):
if len(config) > 0:
if level > 0:
lines += '\n'
bet_lines = []
for key, item in config.items():
bet_lines.append('{indent}{key}: {value}'.format(
indent=''.rjust(level * indent, ' '),
key=key.strip('"'), # for dotted keys enclosed with "" to not be interpreted as nested key,
value=cls.to_yaml(item, compact, indent, level + 1))
)
lines += '\n'.join(bet_lines)
elif isinstance(config, list):
config_list = [line for line in config if line is not None]
if len(config_list) == 0:
lines += '[]'
else:
lines += '\n'
bet_lines = []
for item in config_list:
bet_lines.append('{indent}- {value}'.format(indent=''.rjust(level * indent, ' '),
value=cls.to_yaml(item, compact, indent, level + 1)))
lines += '\n'.join(bet_lines)
elif isinstance(config, basestring):
# if it contains a \n then it's multiline
lines = config.split('\n')
if len(lines) == 1:
lines = config
else:
lines = '|\n' + '\n'.join([line.rjust(level * indent, ' ') for line in lines])
elif config is None or isinstance(config, NoneValue):
lines = 'null'
elif config is True:
lines = 'true'
elif config is False:
lines = 'false'
else:
lines = str(config)
return lines

@classmethod
def to_properties(cls, config, compact=False, indent=2, key_stack=[]):
"""Convert HOCON input into a .properties output
:return: .properties string representation
:type return: basestring
:return:
"""

def escape_value(value):
return value.replace('=', '\\=').replace('!', '\\!').replace('#', '\\#').replace('\n', '\\\n')

stripped_key_stack = [key.strip('"') for key in key_stack]
lines = []
if isinstance(config, ConfigTree):
for key, item in config.items():
if item is not None:
lines.append(cls.to_properties(item, compact, indent, stripped_key_stack + [key]))
elif isinstance(config, list):
for index, item in enumerate(config):
if item is not None:
lines.append(cls.to_properties(item, compact, indent, stripped_key_stack + [str(index)]))
elif isinstance(config, basestring):
lines.append('.'.join(stripped_key_stack) + ' = ' + escape_value(config))
elif config is True:
lines.append('.'.join(stripped_key_stack) + ' = true')
elif config is False:
lines.append('.'.join(stripped_key_stack) + ' = false')
elif config is None or isinstance(config, NoneValue):
pass
else:
lines.append('.'.join(stripped_key_stack) + ' = ' + str(config))
return '\n'.join([line for line in lines if len(line) > 0])

@classmethod
def convert(cls, config, output_format='json', indent=2, compact=False):
converters = {
'json': cls.to_json,
'properties': cls.to_properties,
'yaml': cls.to_yaml,
'hocon': cls.to_hocon,
}

if output_format in converters:
return converters[output_format](config, compact, indent)
else:
raise Exception("Invalid format '{format}'. Format must be 'json', 'properties', 'yaml' or 'hocon'".format(
format=output_format))

@classmethod
def convert_from_file(cls, input_file=None, output_file=None, output_format='json', indent=2, compact=False):
"""Convert to json, properties or yaml
:param input_file: input file, if not specified stdin
:param output_file: output file, if not specified stdout
:param output_format: json, properties or yaml
:return: json, properties or yaml string representation
"""

if input_file is None:
content = sys.stdin.read()
config = ConfigFactory.parse_string(content)
else:
config = ConfigFactory.parse_file(input_file)

res = cls.convert(config, output_format, indent, compact)
if output_file is None:
print(res)
else:
with open(output_file, "w") as fd:
fd.write(res)
Loading

0 comments on commit 9ca1bf4

Please sign in to comment.