Skip to content

Commit

Permalink
Initial commit of working parser
Browse files Browse the repository at this point in the history
  • Loading branch information
virtuald committed Sep 5, 2014
0 parents commit 14be41a
Show file tree
Hide file tree
Showing 53 changed files with 1,587 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
dist
build
__pycache__
*.pyc
.coverage
*.egg-info

.project
.pydevproject
.settings

src/hcl/parsetab.dat
354 changes: 354 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
include requirements.txt
include testing-requirements.txt
include README.md
include LICENSE

include tests/*.py
include tests/fixtures/*.hcl
include tests/fixtures/*.json
include tests/lex-fixtures/*.hcl
include tests/lex-fixtures/*.json

75 changes: 75 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
pyhcl
=====

Implements a parser of the HashiCorp Configuration Language in Python. This
implementation aims to be compatible with the original golang version of the
parser.

The grammar and many of the tests/fixtures were copied/ported from the golang
parser into pyhcl.

Installation
============

pip install pyhcl

Usage
=====

This module is intended to be used in mostly the same way that one would use
the json module in python, and load/loads/dumps are implemented.

import hcl

with open('file.hcl', r) as fp:
obj = hcl.load(fp)

Currently the dumps function outputs JSON, and not HCL.

Syntax
======

* Single line comments start with `#` or `//`

* Multi-line comments are wrapped in `/*` and `*/`

* Values are assigned with the syntax `key = value` (whitespace doesn't
matter). The value can be any primitive: a string, number, boolean,
object, or list.

* Strings are double-quoted and can contain any UTF-8 characters.
Example: `"Hello, World"`

* Numbers are assumed to be base 10. If you prefix a number with 0x,
it is treated as a hexadecimal. If it is prefixed with 0, it is
treated as an octal. Numbers can be in scientific notation: "1e10".

* Boolean values: `true`, `false`, `on`, `off`, `yes`, `no`.

* Arrays can be made by wrapping it in `[]`. Example:
`["foo", "bar", 42]`. Arrays can contain primitives
and other arrays, but cannot contain objects. Objects must
use the block syntax shown below.

Objects and nested objects are created using the structure shown below:

```
variable "ami" {
description = "the AMI to use"
}
```



Testing
=======

To run the tests:

cd tests
py.test

Authors
=======

Dustin Spicuzza ([email protected])
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ply
45 changes: 45 additions & 0 deletions scripts/hcltool
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python

"""Command-line tool to validate HCL and pretty-print JSON from it
Usage::
$ echo '{"json":"obj"}' | hcltool
{
"json": "obj"
}
$ echo '{ 1.2:3.4}' | python -m json.tool
Expecting property name enclosed in double quotes: line 1 column 3 (char 2)
Copy/pasted from json.tool, distributed under the python license.
"""

import sys
import json
import hcl

def main():
if len(sys.argv) == 1:
infile = sys.stdin
outfile = sys.stdout
elif len(sys.argv) == 2:
infile = open(sys.argv[1], 'rb')
outfile = sys.stdout
elif len(sys.argv) == 3:
infile = open(sys.argv[1], 'rb')
outfile = open(sys.argv[2], 'wb')
else:
raise SystemExit(sys.argv[0] + " [infile [outfile]]")
with infile:
try:
obj = hcl.load(infile)
except ValueError as e:
raise SystemExit(e)
with outfile:
json.dump(obj, outfile, sort_keys=True,
indent=4, separators=(',', ': '))
outfile.write('\n')


if __name__ == '__main__':
main()
47 changes: 47 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env python

from os.path import dirname, join
from distutils.core import setup

try:
from setuptools.command.install import install as _install
except ImportError:
from distutils.command.install import install as _install

import sys

setup_dir = dirname(__file__)

def _post_install():
'''Initialize the parse table at install time'''
import hcl
from hcl.parser import HclParser
parser = HclParser()


class install(_install):
def run(self):
_install.run(self)
self.execute(_post_install, (), msg="Generating parse table...")


def get_version():
d = {}
with open(join(setup_dir, 'src', 'hcl', 'version.py')) as fp:
exec(compile(fp.read(), 'version.py', 'exec'), {}, d)
return d['__version__']

install_requires=open(join(setup_dir, 'requirements.txt')).readlines()

setup(name='pyhcl',
version=get_version(),
description='HCL configuration parser for python',
author='Dustin Spicuzza',
author_email='[email protected]',
package_dir={'': 'src'},
packages=['hcl'],
scripts=["scripts/hcltool"],
install_requires=install_requires,
cmdclass={'install': install})


3 changes: 3 additions & 0 deletions src/hcl/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

from .api import dumps, load, loads
from .version import __version__
41 changes: 41 additions & 0 deletions src/hcl/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

import json
from .parser import HclParser

import sys


def isHcl(s):
for c in s:
if c.isspace():
continue

if c == '{':
return False
else:
return True

raise ValueError("No HCL object could be decoded")


def load(fp):
return loads(fp.read())

def loads(s):
'''
Creates a dictionary out of an HCL-formatted string or a JSON string
TODO: Multiple threads
'''
if isHcl(s):
if sys.version_info[0] < 3 and isinstance(s, unicode):
s = unicode(s, 'utf-8')
return HclParser().parse(s)
else:
return json.loads(s)


def dumps(*args, **kwargs):
'''Turns a dictionary into JSON, passthru to json.dumps'''
return json.dumps(*args, **kwargs)

Loading

0 comments on commit 14be41a

Please sign in to comment.