Skip to content

Commit

Permalink
feat chaotic: allow object in root of file
Browse files Browse the repository at this point in the history
  • Loading branch information
lirik90 committed Dec 6, 2024
1 parent 8d5b2f2 commit 4f978f5
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 28 deletions.
42 changes: 40 additions & 2 deletions chaotic/chaotic/front/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import contextlib
import dataclasses
import os
import re
from typing import Dict
from typing import Generator
from typing import List
Expand All @@ -13,6 +14,27 @@
from chaotic.front import types


@dataclasses.dataclass(init=False)
class NameMapItem:
pattern: re.Pattern
dest: str # string for str.format()

def __init__(self, data: str):
groups = data.split('=')
if len(groups) != 2:
raise Exception(f'-n arg must contain "=" ({data})')

pattern, dest = groups
self.pattern = re.compile(pattern)
self.dest = dest

def match(self, data: str) -> Optional[str]:
match = self.pattern.fullmatch(data) # pylint: disable=no-member
if match:
return self.dest.format(*match.groups())
return None


@dataclasses.dataclass(frozen=True)
class ParserConfig:
erase_prefix: str
Expand All @@ -33,14 +55,17 @@ class ParserError(error.BaseError):

class SchemaParser:
def __init__(
self, *, config: ParserConfig, full_filepath: str, full_vfilepath: str,
self, *, config: ParserConfig,
full_filepath: str, full_vfilepath: str,
plain_object_path_map: List[NameMapItem] = None,
) -> None:
self._config = config
# Full filepath on real filesystem
self.full_filepath: str = full_filepath
# Full filepath on virtual filesystem, used by $ref
self.full_vfilepath: str = full_vfilepath
self._state = ParserState(infile_path='', schemas=dict())
self._plain_object_path_map = plain_object_path_map

def parse_schema(self, infile_path: str, input__: dict) -> None:
self._state.infile_path = ''
Expand Down Expand Up @@ -312,7 +337,20 @@ def _make_abs_ref(self, ref: str) -> str:
return self.full_vfilepath + ref
else:
my_ref = '/'.join(self.full_vfilepath.split('/')[:-1])
file, infile = ref.split('#')
if '#' in ref:
file, infile = ref.split('#')
else:
file = ref
if file.startswith('./'):
file = file[2:]

for item in self._plain_object_path_map:
infile = item.match(ref)
if infile is not None:
infile = infile.rstrip('/')
break
if infile is None:
self._raise(f'Invalid ref without in-file path')
out_file = os.path.join(my_ref, file)
# print(f'ref: {out_file} # {infile}')
return out_file + '#' + infile
Expand Down
50 changes: 24 additions & 26 deletions chaotic/chaotic/main.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import argparse
import dataclasses
import os
import pathlib
import re
import sys
from typing import Any
from typing import Callable
from typing import Dict
from typing import List
from typing import Optional

import yaml

Expand All @@ -17,27 +14,7 @@
from chaotic.front import parser as front_parser
from chaotic.front import ref_resolver
from chaotic.front import types


@dataclasses.dataclass(init=False)
class NameMapItem:
pattern: re.Pattern
dest: str # string for str.format()

def __init__(self, data: str):
groups = data.split('=')
if len(groups) != 2:
raise Exception(f'-n arg must contain "=" ({data})')

pattern, dest = groups
self.pattern = re.compile(pattern)
self.dest = dest

def match(self, data: str) -> Optional[str]:
match = self.pattern.fullmatch(data) # pylint: disable=no-member
if match:
return self.dest.format(*match.groups())
return None
from chaotic.front.parser import NameMapItem


def parse_args() -> argparse.Namespace:
Expand All @@ -60,6 +37,13 @@ def parse_args() -> argparse.Namespace:
help='full filepath to virtual filepath mapping',
)

parser.add_argument(
'--plain-object-path-map',
type=NameMapItem,
action='append',
help='plain object filepath to in-file path',
)

parser.add_argument(
'-u',
'--userver',
Expand Down Expand Up @@ -174,9 +158,17 @@ def traverse_dfs(path: str, data: Any):

def extract_schemas_to_scan(
inp: dict, name_map: List[NameMapItem],
fname: str, plain_object_path_map: List[NameMapItem],
) -> Dict[str, Any]:
schemas = []

if plain_object_path_map is not None and ('type' in inp) and inp['type'] == 'object':
for item in plain_object_path_map:
path = item.match(fname)
if path is not None:
schemas.append((path, inp))
return dict(schemas)

gen = traverse_dfs('/', inp)
ok_ = None
while True:
Expand All @@ -201,6 +193,7 @@ def read_schemas(
name_map,
file_map,
dependencies: List[types.ResolvedSchemas] = [],
plain_object_path_map: List[NameMapItem] = None,
) -> types.ResolvedSchemas:
config = front_parser.ParserConfig(erase_prefix=erase_path_prefix)
rr = ref_resolver.RefResolver()
Expand All @@ -210,11 +203,12 @@ def read_schemas(
with open(fname) as ifile:
data = yaml.load(ifile, Loader=yaml.CLoader)

scan_objects = extract_schemas_to_scan(data, name_map)
scan_objects = extract_schemas_to_scan(data, name_map, fname, plain_object_path_map)

vfilepath = vfilepath_from_filepath(fname, file_map)
parser = front_parser.SchemaParser(
config=config, full_filepath=fname, full_vfilepath=vfilepath,
plain_object_path_map=plain_object_path_map,
)
for path, obj in rr.sort_json_types(
scan_objects, erase_path_prefix,
Expand Down Expand Up @@ -246,7 +240,11 @@ def main() -> None:
args = parse_args()

schemas = read_schemas(
args.erase_path_prefix, args.file, args.name_map, args.file_map,
erase_path_prefix=args.erase_path_prefix,
filepaths=args.file,
name_map=args.name_map,
file_map=args.file_map,
plain_object_path_map=args.plain_object_path_map,
)
cpp_name_func = generate_cpp_name_func(
args.name_map, args.erase_path_prefix,
Expand Down

0 comments on commit 4f978f5

Please sign in to comment.