From 4ba09b15487d51e11b6dce0b690841130d357ed8 Mon Sep 17 00:00:00 2001 From: podhmo Date: Thu, 20 Oct 2016 02:26:29 +0900 Subject: [PATCH] mixin examples --- Makefile | 28 ++++- README.rst | 5 +- example4.rst | 170 +++++++++++++++++++++++++++++ examples/mixin/main.yaml | 47 ++++++++ examples/mixin/swagger-bundler.ini | 19 ++++ examples/mixin/transform.py | 25 +++++ swagger_bundler/postscript.py | 61 +++++++---- 7 files changed, 334 insertions(+), 21 deletions(-) create mode 100644 example4.rst create mode 100644 examples/mixin/main.yaml create mode 100644 examples/mixin/swagger-bundler.ini create mode 100644 examples/mixin/transform.py diff --git a/Makefile b/Makefile index c536490..78e0bcb 100644 --- a/Makefile +++ b/Makefile @@ -78,5 +78,31 @@ example3: cat .tmp/02* | gsed 's/^/ /g' >> example3.rst rm -r .tmp -.PHONY: example example3 +example4: + rm -rf .tmp; mkdir -p .tmp + tree examples/mixin > .tmp/00structure.txt + for i in `find examples/mixin/ -name "*.yaml" -type f`; do cat $$i > .tmp/01`echo $$i | tr '/' '__'`; done + for i in `find examples/mixin/ -name "*.py" -type f`; do cat $$i > .tmp/02`echo $$i | tr '/' '__'`; done + (cd examples/mixin; swagger-bundler bundle main.yaml) > .tmp/03generated.yaml + rm -f example4.rst; touch example4.rst + echo "# structure" >> example4.rst + echo "\n.. code-block:: bash" >> example4.rst + echo "" >> example4.rst + echo "$ tree" | gsed 's/^/ /g' >> example4.rst + cat .tmp/00* | gsed 's/^/ /g' >> example4.rst + echo "$ swagger-bundler bundle main.yaml > generated.yaml" | gsed 's/^/ /g' >> example4.rst + echo "\n## swagger-bundler.ini(config file)" >> example4.rst + echo "\n.. code-block::" >> example4.rst + echo "" >> example4.rst + cat examples/mixin/swagger-bundler.ini | gsed 's/^/ /g' >> example4.rst + for i in `ls .tmp/02* | grep -v generated.py`; do echo "\n" >> example4.rst; echo `echo $${i} | gsed 's@^.tmp/02examples_@@g; s@__*@/@g;'` >> example4.rst; echo "\n.. code-block:: python\n" >> example4.rst; cat $${i} | gsed 's/^/ /g' >> example4.rst; done + echo "\n" >> example4.rst + for i in `ls .tmp/01* | grep -v generated.yaml`; do echo "\n" >> example4.rst; echo `echo $${i} | gsed 's@^.tmp/01examples_@@g; s@__*@/@g;'` >> example4.rst; echo "\n.. code-block:: yaml\n" >> example4.rst; cat $${i} | gsed 's/^/ /g' >> example4.rst; done + echo "\n" >> example4.rst + echo "## generated.yaml" >> example4.rst + echo "\n.. code-block:: yaml\n" >> example4.rst + cat .tmp/03* | gsed 's/^/ /g' >> example4.rst + rm -r .tmp + +.PHONY: example example4 .PHONY: watch updatespec test regenerate diff --git a/README.rst b/README.rst index 2e063f5..aafed3b 100644 --- a/README.rst +++ b/README.rst @@ -154,7 +154,10 @@ appendix: hook configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -the link of `example(hook configuration, lifting sub definitions) `_ +Hook configuration examples. + +- `lifting sub definitions sample `_ +- `tiny individual mixin sample `_ watch option ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/example4.rst b/example4.rst new file mode 100644 index 0000000..b42599b --- /dev/null +++ b/example4.rst @@ -0,0 +1,170 @@ +# structure + +.. code-block:: bash + + tree + examples/mixin + ├── __pycache__ + │   └── transform.cpython-35.pyc + ├── main.yaml + ├── swagger-bundler.ini + └── transform.py + + 1 directory, 4 files + swagger-bundler bundle main.yaml > generated.yaml + +## swagger-bundler.ini(config file) + +.. code-block:: + + [DEFAULT] + + [special_marker] + # todo: gentle description. + namespace = x-bundler-namespace + compose = x-bundler-compose + concat = x-bundler-concat + exposed = x-bundler-exposed + + [postscript_hook] + # lambda ctx, data, *args, **kwargs: do_something() + ## examples: + # swagger_bundler.postscript:echo + # or + # a/b/c/d.py:function_name + compose = + bundle = ./transform.py:activate_mixin + add_namespace = + validate = + + +mixin/transform.py + +.. code-block:: python + + from swagger_bundler import highlight + from swagger_bundler.postscript import LooseDictWalker + + + def activate_mixin(ctx, data, marker="x-bundler-mixin", pop_path_list=["x-bundler-types"], *args, **kwargs): + if not kwargs.get("last"): + return data + + def emit_mixin(subdata): + path_list = subdata.pop(marker, None) + if isinstance(path_list, (str, bytes)): + path_list = [path_list] + for path in path_list: + if not path.startswith("#"): + highlight.show_on_warning("mixin: path={!r} is not found".format(path)) + continue + target = data + for name in path.lstrip("#").split("/"): + if name: + target = target[name] + subdata.update(target) + w = LooseDictWalker(on_container=emit_mixin) + w.walk([marker], data) + for path in pop_path_list: + data.pop(path, None) + + + + +mixin/main.yaml + +.. code-block:: yaml + + x-bundler-types: + id: + pattern: '[0-9a-f]{24}' + s: + type: string + + + definitions: + pair: + type: object + properties: + left: + type: string + x-bundler-mixin: '#/x-bundler-types/id' + right: + type: string + x-bundler-mixin: '#/x-bundler-types/id' + id: + x-bundler-mixin: ['#/x-bundler-types/id', '#/x-bundler-types/s'] + description: object id + + parameters: + siteId: + x-bundler-mixin: ['#/x-bundler-types/id', '#/x-bundler-types/s'] + + + paths: + /sites/{siteId}: + PATCH: + operationId: updateSite + parameters: + - name: siteId + in: path + required: true + x-bundler-mixin: ['#/x-bundler-types/id', '#/x-bundler-types/s'] + - name: body + in: body + schema: + type: object + properties: + url: + $ref: "#/definitions/id" + userId: + $ref: "#/definitions/id" + responses: + 200: + description: OK + + +## generated.yaml + +.. code-block:: yaml + + definitions: + pair: + type: object + properties: + left: + type: string + pattern: '[0-9a-f]{24}' + right: + type: string + pattern: '[0-9a-f]{24}' + id: + description: object id + pattern: '[0-9a-f]{24}' + type: string + paths: + /sites/{siteId}: + PATCH: + operationId: updateSite + parameters: + - name: siteId + in: path + required: true + pattern: '[0-9a-f]{24}' + type: string + - name: body + in: body + schema: + type: object + properties: + url: + $ref: '#/definitions/id' + userId: + $ref: '#/definitions/id' + responses: + 200: + description: OK + parameters: + siteId: + pattern: '[0-9a-f]{24}' + type: string diff --git a/examples/mixin/main.yaml b/examples/mixin/main.yaml new file mode 100644 index 0000000..8ab6fdf --- /dev/null +++ b/examples/mixin/main.yaml @@ -0,0 +1,47 @@ +x-bundler-types: + id: + pattern: '[0-9a-f]{24}' + s: + type: string + + +definitions: + pair: + type: object + properties: + left: + type: string + x-bundler-mixin: '#/x-bundler-types/id' + right: + type: string + x-bundler-mixin: '#/x-bundler-types/id' + id: + x-bundler-mixin: ['#/x-bundler-types/id', '#/x-bundler-types/s'] + description: object id + +parameters: + siteId: + x-bundler-mixin: ['#/x-bundler-types/id', '#/x-bundler-types/s'] + + +paths: + /sites/{siteId}: + PATCH: + operationId: updateSite + parameters: + - name: siteId + in: path + required: true + x-bundler-mixin: ['#/x-bundler-types/id', '#/x-bundler-types/s'] + - name: body + in: body + schema: + type: object + properties: + url: + $ref: "#/definitions/id" + userId: + $ref: "#/definitions/id" + responses: + 200: + description: OK diff --git a/examples/mixin/swagger-bundler.ini b/examples/mixin/swagger-bundler.ini new file mode 100644 index 0000000..ced459a --- /dev/null +++ b/examples/mixin/swagger-bundler.ini @@ -0,0 +1,19 @@ +[DEFAULT] + +[special_marker] +# todo: gentle description. +namespace = x-bundler-namespace +compose = x-bundler-compose +concat = x-bundler-concat +exposed = x-bundler-exposed + +[postscript_hook] +# lambda ctx, data, *args, **kwargs: do_something() +## examples: +# swagger_bundler.postscript:echo +# or +# a/b/c/d.py:function_name +compose = +bundle = ./transform.py:activate_mixin +add_namespace = +validate = diff --git a/examples/mixin/transform.py b/examples/mixin/transform.py new file mode 100644 index 0000000..0e65d95 --- /dev/null +++ b/examples/mixin/transform.py @@ -0,0 +1,25 @@ +from swagger_bundler import highlight +from swagger_bundler.postscript import LooseDictWalker + + +def activate_mixin(ctx, data, marker="x-bundler-mixin", pop_path_list=["x-bundler-types"], *args, **kwargs): + if not kwargs.get("last"): + return data + + def emit_mixin(subdata): + path_list = subdata.pop(marker, None) + if isinstance(path_list, (str, bytes)): + path_list = [path_list] + for path in path_list: + if not path.startswith("#"): + highlight.show_on_warning("mixin: path={!r} is not found".format(path)) + continue + target = data + for name in path.lstrip("#").split("/"): + if name: + target = target[name] + subdata.update(target) + w = LooseDictWalker(on_container=emit_mixin) + w.walk([marker], data) + for path in pop_path_list: + data.pop(path, None) diff --git a/swagger_bundler/postscript.py b/swagger_bundler/postscript.py index acbcd3a..c2784ce 100644 --- a/swagger_bundler/postscript.py +++ b/swagger_bundler/postscript.py @@ -23,7 +23,7 @@ def add_responses_default(ctx, data, *args, **kwargs): def add_default(response): if not any(x in response for x in ["default", "$ref"]): response["default"] = {"$ref": "::default::"} - fix_data_in_target_section(deque(["paths", "responses"]), data, add_default) + loose_dict_walker(deque(["paths", "responses"]), data, add_default) return data @@ -36,24 +36,47 @@ def lifting_definition(ctx, data, *args, **kwargs): data["definitions"].update(reversed(extracted.items())) -def fix_data_in_target_section(paths, d, fn): - if hasattr(d, "keys"): - for k in list(d.keys()): - if len(paths) > 0 and paths[0] == k: - name = paths.popleft() - if len(paths) == 0: - fn(d[k]) - fix_data_in_target_section(paths, d[k], fn) - paths.appendleft(name) - else: - fix_data_in_target_section(paths, d[k], fn) - return d - elif isinstance(d, (list, tuple)): - for e in d: - fix_data_in_target_section(paths, e, fn) - return d - else: - return d +class LooseDictWalker: + def __init__(self, on_container=None, on_data=None): + self.on_container = on_container + self.on_data = on_data + + def on_found(self, d, k): + if self.on_container is not None: + self.on_container(d) + if self.on_data is not None: + self.on_data(d[k]) + + def walk(self, paths, d): + return self._walk(deque(paths), d) + + def _walk(self, paths, d): + if hasattr(d, "keys"): + for k in list(d.keys()): + if len(paths) > 0 and paths[0] == k: + name = paths.popleft() + self._walk(paths, d[k]) + if len(paths) == 0: + self.on_found(d, k) + paths.appendleft(name) + else: + self._walk(paths, d[k]) + return d + elif isinstance(d, (list, tuple)): + for e in d: + self._walk(paths, e) + return d + else: + return d + + +def loose_dict_walker(paths, d, fn): + w = LooseDictWalker(on_data=fn) + return w.walk(paths, d) + + +# for backward compatibility +fix_data_in_target_section = loose_dict_walker class ExtractorContext: