Skip to content

Commit

Permalink
Added parse and pickle subcommands, and allow those files to the conv…
Browse files Browse the repository at this point in the history
…ert commmand.
  • Loading branch information
eerimoq committed May 31, 2018
1 parent 8565412 commit a2d21dc
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 47 deletions.
39 changes: 39 additions & 0 deletions README.rst
Expand Up @@ -194,6 +194,45 @@ dates.
}
>
The parse subcommand
^^^^^^^^^^^^^^^^^^^^

Parse given ASN.1 specification and write it as a Python dictionary to
given file. Use the created file to convert given encoded Question
from BER to GSER (produces human readable text). The conversion is
significantly faster than passing .asn-file(s) to the convert
subcommand, especially for larger ASN.1 specifications.

.. code-block:: text
> asn1tools parse tests/files/foo.asn foo.py
> asn1tools convert foo.py Question 300e0201011609497320312b313d333f
question Question ::= {
id 1,
question "Is 1+1=3?"
}
>
The pickle subcommand
^^^^^^^^^^^^^^^^^^^^^

Compile and pickle given ASN.1 specification with BER and XER
codecs. Use the created files to convert given encoded Question from
BER to XER (produces human readable text). The conversion is
significantly faster than passing .asn-file(s) to the convert
subcommand, especially for larger ASN.1 specifications.

.. code-block:: text
> asn1tools pickle --codec ber tests/files/foo.asn foo-ber.pkl
> asn1tools pickle --codec xer tests/files/foo.asn foo-xer.pkl
> asn1tools convert -o xer foo-ber.pkl foo-xer.pkl Question 300e0201011609497320312b313d333f
<Question>
<id>1</id>
<question>Is 1+1=3?</question>
</Question>
>
Contributing
============

Expand Down
103 changes: 92 additions & 11 deletions asn1tools/__init__.py
Expand Up @@ -8,6 +8,8 @@
import argparse
import binascii
import logging
from pprint import pformat
import pickle

from prompt_toolkit.contrib.completers import WordCompleter
from prompt_toolkit import prompt
Expand All @@ -30,7 +32,7 @@


__author__ = 'Erik Moqvist'
__version__ = '0.91.0'
__version__ = '0.92.0'


class ArgumentParserError(Error):
Expand All @@ -53,6 +55,21 @@ def error(self, message):
raise ArgumentParserError(message)


def import_module(pyfilepath):
module_name = os.path.splitext(os.path.basename(pyfilepath))[0]

if sys.version_info > (3, 4):
import importlib.util
spec = importlib.util.spec_from_file_location(module_name, pyfilepath)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
else:
import imp
module = imp.load_source(module_name, pyfilepath)

return module


def _convert_hexstring(input_spec,
output_spec,
output_codec,
Expand All @@ -73,10 +90,31 @@ def _convert_hexstring(input_spec,
print(decoded.decode('latin-1'))


def _compile_files(specifications, input_codec, output_codec):
parsed = parse_files(specifications)
input_spec = compile_dict(parsed, input_codec)
output_spec = compile_dict(parsed, output_codec)
def _compile_files(specs, input_codec, output_codec):
py_count = [spec.endswith('.py') for spec in specs].count(True)
pkl_count = [spec.endswith('.pkl') for spec in specs].count(True)

if py_count > 0:
if py_count != 1:
raise Exception('Expected one .py-file, but got {}.'.format(py_count))

module = import_module(specs[0])
parsed = module.SPECIFICATION
input_spec = compile_dict(parsed, input_codec)
output_spec = compile_dict(parsed, output_codec)
elif pkl_count > 0:
if pkl_count != 2:
raise Exception('Expected two .pkl-files, but got {}.'.format(pkl_count))

with open(specs[0], 'rb') as fin:
input_spec = pickle.load(fin)

with open(specs[1], 'rb') as fin:
output_spec = pickle.load(fin)
else:
parsed = parse_files(specs)
input_spec = compile_dict(parsed, input_codec)
output_spec = compile_dict(parsed, output_codec)

return input_spec, output_spec

Expand Down Expand Up @@ -138,6 +176,7 @@ def _handle_command_compile(line):
return input_spec, output_spec, args.output_codec
except Exception as e:
print('error: {}'.format(str(e)))

return None, None, None


Expand Down Expand Up @@ -217,6 +256,20 @@ def _do_shell(_args):
print('{}: command not found'.format(line))


def _do_parse(args):
parsed = parse_files(args.specification)

with open(args.outfile, 'w') as fout:
fout.write('SPECIFICATION = {}'.format(pformat(parsed)))


def _do_pickle(args):
compiled = compile_files(args.specification, args.codec)

with open(args.outfile, 'wb') as fout:
pickle.dump(compiled, fout)


def _main():
parser = argparse.ArgumentParser(
description='Various ASN.1 utilities.')
Expand Down Expand Up @@ -250,19 +303,47 @@ def _main():
choices=('ber', 'der', 'jer', 'per', 'uper', 'xer', 'gser'),
default='gser',
help='Output format (default: gser).')
subparser.add_argument('specification',
nargs='+',
help='ASN.1 specification as one or more .asn files.')
subparser.add_argument(
'specification',
nargs='+',
help=('ASN.1 specification as one or more .asn files, one .py file or '
'one input and one output codec .pkl files. The .py-file may be '
'created with the parse subcommand. The .pkl-files may be '
'created with the pickle subcommand.'))
subparser.add_argument('type', help='Type to convert.')
subparser.add_argument(
'hexstring',
help='Hexstring to convert, or - to read hexstrings from standard input.')
subparser.set_defaults(func=_do_convert)

# The 'shell' subparser.
shell_parser = subparsers.add_parser('shell',
description='An interactive shell.')
shell_parser.set_defaults(func=_do_shell)
subparser = subparsers.add_parser('shell',
description='An interactive shell.')
subparser.set_defaults(func=_do_shell)

# The 'parse' subparser.
subparser = subparsers.add_parser('parse',
description='Convert to a Python dictionary.')
subparser.add_argument('specification',
nargs='+',
help='ASN.1 specification as one or more .asn files.')
subparser.add_argument('outfile',
help='Output file name.')
subparser.set_defaults(func=_do_parse)

# The 'pickle' subparser.
subparser = subparsers.add_parser('pickle',
description='Pickle.')
subparser.add_argument('-c', '--codec',
choices=('ber', 'der', 'jer', 'per', 'uper', 'xer', 'gser'),
default='ber',
help='Codec (default: ber).')
subparser.add_argument('specification',
nargs='+',
help='ASN.1 specification as one or more .asn files.')
subparser.add_argument('outfile',
help='Output file name.')
subparser.set_defaults(func=_do_pickle)

args = parser.parse_args()

Expand Down
36 changes: 0 additions & 36 deletions tests/test_command_line.py
Expand Up @@ -18,42 +18,6 @@ class Asn1ToolsCommandLineTest(unittest.TestCase):

maxDiff = None

def test_command_line_help(self):
argv = ['asn1tools', '--help']

stdout = StringIO()

with patch('sys.stdout', stdout):
with patch('sys.argv', argv):
with self.assertRaises(SystemExit):
asn1tools._main()

expected_output = [
"usage: asn1tools [-h] [-d] [-v {0,1,2}] [--version] {convert,shell} ...",
"",
"Various ASN.1 utilities.",
"",
"optional arguments:",
" -h, --help show this help message and exit",
" -d, --debug",
" -v {0,1,2}, --verbose {0,1,2}",
" Control the verbosity; ",
"disable(0),",
"warning(1)",
"and",
"debug(2).",
"(default: 1).",
" --version Print version information and exit.",
"",
"subcommands:",
" {convert,shell}"
]

print(stdout.getvalue())

for line in expected_output:
self.assertIn(line, stdout.getvalue())

def test_command_line_convert_ber_foo_question(self):
argv = [
'asn1tools',
Expand Down

0 comments on commit a2d21dc

Please sign in to comment.