Skip to content

Commit

Permalink
add version numbering
Browse files Browse the repository at this point in the history
  • Loading branch information
felix-andreas committed May 3, 2020
1 parent beee6c9 commit dcb7c47
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 34 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ A LatticeJSON file for a FODO lattice:

```json
{
"version": "2.0",
"title": "FODO Lattice",
"info": "This is the simplest possible strong focusing lattice.",
"root": "RING",
Expand Down
16 changes: 8 additions & 8 deletions latticejson/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
from . import __version__, io, parse
from .format import format_json
from .migrate import migrate as _migrate
from .validate import validate_file
from .validate import schema, validate_file

FORMATS = "json", "lte", "madx"


@click.group(context_settings=dict(max_content_width=120))
@click.version_option(__version__)
@click.version_option(
message=(f"LatticeJSON CLI, version {__version__}\n{schema['title']}")
)
def cli():
pass

Expand Down Expand Up @@ -72,14 +74,12 @@ def autoformat(files, dry_run):

@cli.command()
@click.argument("file", type=click.Path(exists=True))
@click.option("--from", "from_", required=True, help="Initial version")
@click.option("--to", required=True, help="Final version")
def migrate(file, from_, to):
@click.option("--initial", type=(int, int, int), required=True, help="Initial version")
@click.option("--final", type=(int, int, int), help="Final version (default: latest)")
def migrate(file, initial, final):
"""Migrate old LatticeJSON files to newer versions."""
text = Path(file).read_text()
initial_version = from_.split(".")
final_version = to.split(".")
latticejson = _migrate(json.loads(text), initial_version, final_version)
latticejson = _migrate(json.loads(text), initial.split("."), final.split("."))
click.echo(format_json(latticejson))


Expand Down
9 changes: 8 additions & 1 deletion latticejson/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from .exceptions import UnknownAttributeWarning, UnknownElementWarning
from .parse import parse_elegant, parse_madx
from .validate import schema_version

NAME_MAP = json.loads((Path(__file__).parent / "map.json").read_text())["map"]
TO_ELEGANT = {x: y[0][0] for x, *y in NAME_MAP}
Expand Down Expand Up @@ -60,7 +61,13 @@ def _map_names(lattice_data: dict, name_map: dict):
lattices = lattice_data["lattices"]
root = lattice_data.get("root", tuple(lattices.keys())[-1])
title = lattice_data.get("title", "")
return dict(title=title, root=root, elements=elements, lattices=lattices)
return dict(
version=str(schema_version),
title=title,
root=root,
elements=elements,
lattices=lattices,
)


def to_elegant(latticejson: dict) -> str:
Expand Down
21 changes: 10 additions & 11 deletions latticejson/migrate.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
from typing import Tuple, Optional
from typing import Optional, Tuple

Version = Tuple[int, int, int]


def migrate(data: dict, initial: Version, data: Optional(Version) = None) -> dict:
def migrate(data: dict, initial: Version, final: Optional[Version] = None) -> dict:
data = data.copy()
for from_, to, func in _VERSION_MAPS:
if initial >= from_ and (data is None or data <= to):
if initial >= from_ and (final is None or final <= to):
func(data)
return data


_VERSION_MAPS = (
((0, 0, 2), (0, 0, 3), _0_0_2_to_0_0_3),
((0, 1, 0), (0, 2, 0), _0_1_0_to_0_2_0),
)


def _0_0_2_to_0_0_3(data: dict):
def _0_to_1(data: dict):
data["version"] = "1.0"
elements = data["elements"]
for name, attributes in elements.items():
elements[name] = attributes.pop("type"), attributes


def _0_1_0_to_0_2_0(data: dict):
def _1_to_2(data: dict):
data["version"] = "2.0"
data["title"] = data.pop("name")
data["info"] = data.pop("description")
data["lattices"] = data.pop("sub_lattices")
Expand All @@ -33,3 +29,6 @@ def _0_1_0_to_0_2_0(data: dict):
info = element.pop("description", False)
if info:
element["info"] = info


_VERSION_MAPS = (0, 1, _0_to_1), (1, 2, _1_to_2)
12 changes: 9 additions & 3 deletions latticejson/schema.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
{
"$id": "https://raw.githubusercontent.com/nobeam/latticejson/master/latticejson/schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "JSON lattice file format",
"description": "Defines the magnetic lattice format",
"title": "LatticeJSON Schema, version 2.0",
"description": "A JSON based lattice file format",
"additionalProperties": false,
"required": [
"version",
"root",
"lattices",
"elements"
],
"type": "object",
"properties": {
"version": {
"type": "string",
"pattern": "^2.[0-0]$",
"description": "LatticeJSON version"
},
"title": {
"type": "string",
"description": "The title of the lattice"
Expand Down Expand Up @@ -278,4 +284,4 @@
]
}
}
}
}
37 changes: 27 additions & 10 deletions latticejson/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,36 @@
from pathlib import Path

import fastjsonschema
from packaging import version as _version

from .exceptions import UndefinedObjectError

parse_version = _version.parse
schema_path = Path(__file__).resolve().parent / "schema.json"
schema = json.loads(schema_path.read_text())
schema_version = parse_version(schema["title"][28:])


def validate_file(path: str):
data = json.loads(Path(path).read_text())
validate(data)


def validate(data):
if not "version" in data:
raise Exception("Unknown LatticeJSON version.")

version = parse_version(data["version"])
if version > schema_version:
raise Exception("old version: Use pip install -U latticejson to update.")

if version.major < schema_version.major:
raise Exception('Old version: Use "latticejson migrate" to update file.')

validate_syntax(data)
validate_defined_objects(data)


validate_syntax = fastjsonschema.compile(schema)


Expand All @@ -19,16 +44,8 @@
# return jsonschema.validate(data, schema, types={"array": (list, tuple)})


def validate_file(path: str):
data = json.loads(Path(path).read_text())
validate(data)


def validate(data):
# validate syntax
validate_syntax(data)

# validate lattice file contains all referenced elements and sublattices
def validate_defined_objects(data):
"""Validate whether all referenced elements and sub-lattices are definied."""
elements = data["elements"]
lattices = data["lattices"]

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
author=about["__author__"],
license=about["__license__"],
packages=find_packages(),
install_requires=["fastjsonschema", "click>=7.0", "lark-parser"],
install_requires=["click>=7.0", "fastjsonschema", "lark-parser", "packaging"],
test_requires=["pytest"],
python_requires=">=3.6",
include_package_data=True,
Expand Down
1 change: 1 addition & 0 deletions tests/data/fodo.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"version": "2.0",
"title": "FODO Lattice",
"info": "This is the simplest possible strong focusing lattice.",
"root": "RING",
Expand Down
1 change: 1 addition & 0 deletions tests/data/test_undefined.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"version": "2.0",
"title": "Faulty Fodo Cell",
"info": "This is faulty lattice file. Where elements are referenced but no definiton exists.",
"root": "MAIN",
Expand Down

0 comments on commit dcb7c47

Please sign in to comment.