Repository URL to install this package:
|
Version:
1.26.0.dev0+gite506aa5f ▾
|
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).
import re
from collections import OrderedDict
class Properties:
"""A Python reader for java.util.Properties formatted data.
Based on:
http://download.oracle.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader)
Originally copied from:
https://github.com/twitter/commons/blob/master/src/python/twitter/common/config/properties.py
:API: public
"""
@staticmethod
def load(data):
"""Loads properties from an open stream or the contents of a string.
:API: public
:param (str | open stream) data: An open stream or a string.
:returns: A dict of parsed property data.
:rtype: dict
"""
if hasattr(data, "read") and callable(data.read):
contents = data.read()
elif isinstance(data, str):
contents = data
else:
raise TypeError(f"Can only process data from a str or a readable object, given: {data}")
return Properties._parse(contents.splitlines())
# An unescaped '=' or ':' forms an explicit separator
_EXPLICIT_KV_SEP = re.compile(r"(?<!\\)[=:]")
@staticmethod
def _parse(lines):
def coalesce_lines():
line_iter = iter(lines)
try:
buffer = ""
while True:
line = next(line_iter)
if line.strip().endswith("\\"):
# Continuation.
buffer += line.strip()[:-1]
else:
if buffer:
# Continuation join, preserve left hand ws (could be a kv separator)
buffer += line.rstrip()
else:
# Plain old line
buffer = line.strip()
try:
yield buffer
finally:
buffer = ""
except StopIteration:
pass
def normalize(atom):
return re.sub(r"\\([:=\s])", r"\1", atom.strip())
def parse_line(line):
if line and not (line.startswith("#") or line.startswith("!")):
match = Properties._EXPLICIT_KV_SEP.search(line)
if match:
return normalize(line[: match.start()]), normalize(line[match.end() :])
else:
space_sep = line.find(" ")
if space_sep == -1:
return normalize(line), ""
else:
return normalize(line[:space_sep]), normalize(line[space_sep:])
props = OrderedDict()
for line in coalesce_lines():
kv_pair = parse_line(line)
if kv_pair:
key, value = kv_pair
props[key] = value
return props
@staticmethod
def dump(props, output):
"""Dumps a dict of properties to the specified open stream or file path.
:API: public
"""
def escape(token):
return re.sub(r"([=:\s])", r"\\\1", token)
def write(out):
for k, v in props.items():
out.write(f"{escape(str(k))}={escape(str(v))}\n")
if hasattr(output, "write") and callable(output.write):
write(output)
elif isinstance(output, str):
with open(output, "w+") as out:
write(out)
else:
raise TypeError(f"Can only dump data to a path or a writable object, given: {output}")