Skip to content

Commit e29d0fb

Browse files
committed
Merge remote-tracking branch 'origin/master' into vuepress-website
2 parents 79c5bb8 + 4b14bf5 commit e29d0fb

File tree

11 files changed

+149
-89
lines changed

11 files changed

+149
-89
lines changed

README.md

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This repository contains the
99
[Specification of LatticeJSON](https://github.com/andreasfelix/latticejson/blob/master/latticejson/schema.json)
1010
in form of a [JSON Schema](https://json-schema.org).
1111

12+
1213
## Example
1314

1415
A LatticeJSON file for a FODO lattice:
@@ -36,40 +37,33 @@ A LatticeJSON file for a FODO lattice:
3637
[![PyPI](https://img.shields.io/pypi/v/latticejson.svg)](https://pypi.org/project/latticejson/)
3738
[![CI](https://github.com/andreasfelix/latticejson/workflows/CI/badge.svg)](https://github.com/andreasfelix/latticejson/actions?query=workflow%3ACI)
3839

39-
This repository also contains a Python based commandline tool which is able validate and convert LatticeJSON
40-
files into other common lattice file formats.
40+
This repository also contains a Python based command-line tool which is able validate
41+
and convert LatticeJSON files into other common lattice file formats and vice versa.
4142

4243
You can install and update it using pip or pipenv:
43-
4444
```sh
4545
pip install -U latticejson
4646
```
4747

48-
4948
Validate a LatticeJSON file:
5049
```sh
5150
latticejson validate /path/to/lattice.json
5251
```
5352

54-
Convert a LatticeJSON file into the elegant lattice format:
53+
Convert a elegant lattice format into the LatticeJSON format:
5554
```sh
56-
latticejson convert elegant /path/to/lattice.json
55+
latticejson convert --to json /path/to/lattice.lte
5756
```
5857

5958
To activate Bash completion add
60-
6159
```
6260
eval "$(_LATTICEJSON_COMPLETE=source latticejson)"
6361
```
6462

6563
to your `.bashrc`. Or, create an activation script with:
66-
67-
6864
```
6965
_LATTICEJSON_COMPLETE=source latticejson > latticejson-complete.sh
7066
```
7167

7268
## License
7369
[GNU General Public License v3.0](https://github.com/andreasfelix/latticejson/blob/master/LICENSE)
74-
75-

latticejson/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__title__ = "latticeJSON"
1+
__title__ = "LatticeJSON"
22
__description__ = "A JSON based lattice file format"
33
__url__ = "https://github.com/andreasfelix/latticejson"
44
__version__ = "0.0.2"

latticejson/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .io import read, convert
22
from .validate import validate
33
from .convert import elegant_to_latticejson, latticejson_to_elegant
4-
from .exceptions import UndefinedObjectError
4+
from .migrate import migrate
5+
from .exceptions import UndefinedObjectError, UndefinedRPNVariableError

latticejson/cli.py

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import click
22
import json
33
from pathlib import Path
4+
import itertools
45

56
from .validate import validate_file
6-
from .io import convert_file
7+
from .io import convert as _convert
78
from .parse import parse_elegant as _parse_elegant
9+
from .format import CompactJSONEncoder
10+
from .migrate import migrate as _migrate
11+
12+
13+
FORMATS = "json", "lte"
14+
dump_latticejson = lambda obj: json.dumps(obj, cls=CompactJSONEncoder, indent=4)
815

916

1017
@click.group()
@@ -14,32 +21,73 @@ def main():
1421

1522

1623
@main.command()
17-
@click.argument("output_format")
1824
@click.argument("file", type=click.Path(exists=True))
19-
def convert(**kwargs):
20-
"""Convert a latticeJSON or elegant file into another format."""
21-
output_format = kwargs["output_format"].lower()
22-
if output_format in ("latticejson", "lj", "json"):
23-
output_format = "latticejson"
24-
elif output_format in ("elegant", "ele", "lte"):
25-
output_format = "elegant"
26-
else:
27-
raise Exception(f"Unknown format {output_format}")
25+
@click.option(
26+
"--from",
27+
"from_",
28+
type=click.Choice(FORMATS, case_sensitive=False),
29+
help="Source format [optional, default: use file extension]",
30+
)
31+
@click.option(
32+
"--to",
33+
required=True,
34+
type=click.Choice(FORMATS, case_sensitive=False),
35+
help="Destination format",
36+
)
37+
def convert(file, from_, to):
38+
"""Convert a LatticeJSON or elegant file into another format."""
39+
path = Path(file)
40+
if from_ is None:
41+
from_ = path.suffix[1:]
2842

29-
res = convert_file(kwargs["file"], output_format)
30-
print(res)
43+
click.echo(_convert(path.read_text(), from_, to))
3144

3245

3346
@main.command()
3447
@click.argument("file", type=click.Path(exists=True))
35-
def validate(**kwargs):
36-
"""Validate a latticeJSON lattice file."""
37-
validate_file(kwargs["file"])
48+
def validate(file):
49+
"""Validate a LatticeJSON lattice file."""
50+
validate_file(file)
51+
52+
53+
@main.command()
54+
@click.argument("file", type=click.Path(exists=True))
55+
def parse_elegant(file):
56+
"""Parse elegant file but do not convert to LatticeJSON."""
57+
text = Path(file).read_text()
58+
click.echo(dump_latticejson(_parse_elegant(text)))
59+
60+
61+
@main.command()
62+
@click.argument("files", nargs=-1, type=click.Path(exists=True))
63+
@click.option(
64+
"--dry-run",
65+
"-d",
66+
is_flag=True,
67+
help="Don't write the files back, just output the formatted files.",
68+
)
69+
def autoformat(files, dry_run):
70+
"""Format a LatticeJSON file."""
71+
for path in itertools.chain.from_iterable(
72+
path.rglob("*.json") if path.is_dir() else (path,) for path in map(Path, files)
73+
):
74+
latticejson = json.loads(path.read_text())
75+
formatted = dump_latticejson(latticejson)
76+
click.secho(f"reformatted {path}", bold=True)
77+
if dry_run:
78+
click.echo(formatted)
79+
else:
80+
path.write_text(formatted)
3881

3982

4083
@main.command()
4184
@click.argument("file", type=click.Path(exists=True))
42-
def parse_elegant(**kwargs):
43-
"""Parse elegant file but do not convert to latticeJSON."""
44-
text = Path(kwargs["file"]).read_text()
45-
print(json.dumps(_parse_elegant(text), indent=4))
85+
@click.option("--from", "from_", required=True, help="Initial version")
86+
@click.option("--to", required=True, help="Final version")
87+
def migrate(file, from_, to):
88+
"""Migrate old LatticeJSON files to newer versions."""
89+
text = Path(file).read_text()
90+
initial_version = from_.split(".")
91+
final_version = to.split(".")
92+
latticejson = _migrate(json.loads(text), initial_version, final_version)
93+
click.echo(dump_latticejson(latticejson))

latticejson/convert.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525

2626

2727
def latticejson_to_elegant(lattice_dict) -> str:
28-
"""Convert latticeJSON dict to elegant lattice file format.
29-
:param dict: dict in latticeJSON format
28+
"""Convert LatticeJSON dict to elegant lattice file format.
29+
:param dict: dict in LatticeJSON format
3030
:return: string with in elegant lattice file format
3131
"""
3232
elements = lattice_dict["elements"]
@@ -50,14 +50,14 @@ def latticejson_to_elegant(lattice_dict) -> str:
5050

5151

5252
def elegant_to_latticejson(string):
53-
"""Convert an elegant lattice file to a latticeJSON dict.
53+
"""Convert an elegant lattice file to a LatticeJSON dict.
5454
5555
:param str string: input lattice file as string
5656
:param lattice_name: name of the lattice
5757
:type str, optional
5858
:param description: description of the lattice
5959
:type str, optional
60-
:return: dict in latticeJSON format
60+
:return: dict in LatticeJSON format
6161
"""
6262
elegant_dict = parse_elegant(string)
6363

latticejson/format.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import json
2+
3+
4+
class CompactJSONEncoder(json.JSONEncoder):
5+
"""A JSON Encoder which only indents the first two levels."""
6+
7+
def encode(self, obj, level=0):
8+
if isinstance(obj, dict) and level < 2:
9+
items_indent = (level + 1) * self.indent * " "
10+
items_string = ",\n".join(
11+
f"{items_indent}{json.dumps(key)}: {self.encode(value, level=level+1)}"
12+
for key, value in obj.items()
13+
)
14+
dict_indent = level * self.indent * " "
15+
newline = "\n" if level == 0 else ""
16+
return f"{{\n{items_string}\n{dict_indent}}}{newline}"
17+
else:
18+
return json.dumps(obj)

latticejson/io.py

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,18 @@
66

77

88
def read(text, file_format) -> Dict:
9-
"""Read/validate lattice file and return latticeJSON dict."""
10-
11-
if file_format == ".json":
9+
"""Read/validate lattice file and return LatticeJSON dict."""
10+
if file_format == "json":
1211
latticejson = json.loads(text)
13-
elif file_format == ".lte":
12+
elif file_format == "lte":
1413
latticejson = elegant_to_latticejson(text)
1514
else:
1615
raise NotImplementedError(f"Unkown file file_format {file_format}.")
1716

18-
# validate(latticejson)
17+
validate(latticejson)
1918
return latticejson
2019

2120

22-
def read_file(path) -> Dict:
23-
"""Read lattice file from path
24-
25-
:param path path-like: Path to lattice file
26-
:return: latticeJSON compliant dict.
27-
"""
28-
path = Path(path)
29-
suffix = path.suffix
30-
text = path.read_text()
31-
return read(text, suffix)
32-
33-
3421
def convert(text, input_format, output_format) -> str:
3522
"""Convert lattice file to format.
3623
@@ -42,23 +29,8 @@ def convert(text, input_format, output_format) -> str:
4229
:rtype: str
4330
"""
4431
lattice_json = read(text, input_format)
45-
if output_format == "latticejson":
32+
if output_format == "json":
4633
return json.dumps(lattice_json, indent=4)
47-
elif output_format == "elegant":
34+
elif output_format == "lte":
4835
return latticejson_to_elegant(lattice_json)
49-
5036
raise NotImplementedError(f"Converting to {output_format} is not implemented!")
51-
52-
53-
def convert_file(path, output_format) -> str:
54-
"""Convert a lattice file into `output_format`
55-
56-
:param path path-like: Path to lattice file
57-
:param output_format str: Format of the new lattice file
58-
:return: Returns the new lattice file as string
59-
:rtype: str
60-
"""
61-
path = Path(path)
62-
suffix = path.suffix
63-
text = path.read_text()
64-
return convert(text, suffix, output_format)

latticejson/migrate.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import sys
2+
3+
4+
def migrate(initial: dict, initial_version: tuple, final_version: tuple):
5+
function = getattr(
6+
sys.modules[__name__],
7+
f"migrate_{'_'.join(initial_version)}_to_{'_'.join(final_version)}",
8+
None,
9+
)
10+
if function is None:
11+
raise NotImplementedError(f"Unkown versions {initial_version}, {final_version}")
12+
13+
return function(initial)
14+
15+
16+
def migrate_0_0_2_to_0_0_3(initial: dict):
17+
final = initial.copy()
18+
elements_final = {}
19+
for name, attributes in final["elements"].items():
20+
type_ = attributes.pop("type")
21+
elements_final[name] = type_, attributes
22+
23+
final["elements"] = elements_final
24+
return final

latticejson/rpn.lark

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
%ignore /[ \t\f]/+ // ingore whitespace
1+
%ignore /[ \t\f]/+ // ingore whitespace
22

3-
int: /[+-]?\d+/
4-
float: /[+-]?\d+\.\d*([eE][+-]?\d+)?/ // needed to parse 1.
5-
| /[+-]?\d*\.\d+([eE][+-]?\d+)?/ // needed to parse .1
6-
| /[+-]?\d+[eE][+-]?\d+/ // needed to parse 1e1
3+
INT : ["+" | "-"] DIGIT_PART
4+
FLOAT : ["+" | "-"] (EXP_FLOAT | POINT_FLOAT)
5+
EXP_FLOAT : (DIGIT_PART | POINT_FLOAT) EXPONENT
6+
POINT_FLOAT : [DIGIT_PART] FRACTION | DIGIT_PART "."
7+
DIGIT_PART : /\d+/
8+
FRACTION : "." DIGIT_PART
9+
EXPONENT : ("e" | "E") ["+" | "-"] DIGIT_PART
10+
11+
int : INT
12+
float : FLOAT
713
?number: int | float
814
word: /\w+/
915

latticejson/schema.json

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
}
7070
},
7171
"Element": {
72+
"description": "Abstract base for all elements",
7273
"type": "array",
7374
"items": [
7475
{
@@ -80,13 +81,9 @@
8081
"length"
8182
],
8283
"properties": {
83-
"type": {
84-
"type": "string",
85-
"description": "Type of the element."
86-
},
8784
"description": {
8885
"type": "string",
89-
"description": "Description of the element."
86+
"description": "Optional description of the element."
9087
},
9188
"length": {
9289
"type": "number",

0 commit comments

Comments
 (0)