From 72b0c2065f43f8ee24d4f13ba27fdd386d4406f3 Mon Sep 17 00:00:00 2001
From: bogdan_dm
Date: Tue, 13 Jul 2021 18:48:23 +0300
Subject: [PATCH 1/4] Add yaml and ini parsers
---
json_to_models/cli.py | 70 +-
json_to_models/models/base.py | 2 +-
setup.py | 2 +-
test/test_cli/data/file.ini | 9 +
test/test_cli/data/spotify-swagger.yaml | 2004 +++++++++++++++++++++++
test/test_cli/test_script.py | 10 +-
test/test_cli/test_utils.py | 23 +-
7 files changed, 2091 insertions(+), 29 deletions(-)
create mode 100644 test/test_cli/data/file.ini
create mode 100644 test/test_cli/data/spotify-swagger.yaml
diff --git a/json_to_models/cli.py b/json_to_models/cli.py
index dfbea00..573ea98 100644
--- a/json_to_models/cli.py
+++ b/json_to_models/cli.py
@@ -1,4 +1,5 @@
import argparse
+import configparser
import importlib
import itertools
import json
@@ -10,6 +11,14 @@
from pathlib import Path
from typing import Any, Callable, Dict, Generator, Iterable, List, Tuple, Type, Union
+try:
+ import yaml
+except ImportError:
+ try:
+ import ruamel.yaml as yaml
+ except ImportError:
+ yaml = None
+
from . import __version__ as VERSION
from .dynamic_typing import ModelMeta, register_datetime_classes
from .generator import MetadataGenerator
@@ -80,6 +89,7 @@ def parse_args(self, args: List[str] = None):
(model_name, (lookup, Path(path)))
for model_name, lookup, path in namespace.list or ()
]
+ parser = getattr(FileLoaders, namespace.input_format)
self.output_file = namespace.output
self.enable_datetime = namespace.datetime
disable_unicode_conversion = namespace.disable_unicode_conversion
@@ -94,7 +104,7 @@ def parse_args(self, args: List[str] = None):
dict_keys_fields: List[str] = namespace.dict_keys_fields
self.validate(models, models_lists, merge_policy, framework, code_generator)
- self.setup_models_data(models, models_lists)
+ self.setup_models_data(models, models_lists, parser)
self.set_args(merge_policy, structure, framework, code_generator, code_generator_kwargs_raw,
dict_keys_regex, dict_keys_fields, disable_unicode_conversion)
@@ -157,16 +167,20 @@ def validate(self, models, models_list, merge_policy, framework, code_generator)
elif framework != 'custom' and code_generator is not None:
raise ValueError("--code-generator argument has no effect without '--framework custom' argument")
- def setup_models_data(self, models: Iterable[Tuple[str, Iterable[Path]]],
- models_lists: Iterable[Tuple[str, Tuple[str, Path]]]):
+ def setup_models_data(
+ self,
+ models: Iterable[Tuple[str, Iterable[Path]]],
+ models_lists: Iterable[Tuple[str, Tuple[str, Path]]],
+ parser: 'FileLoaders.T'
+ ):
"""
Initialize lazy loaders for models data
"""
models_dict: Dict[str, List[Iterable[dict]]] = defaultdict(list)
for model_name, paths in models:
- models_dict[model_name].append(map(safe_json_load, paths))
+ models_dict[model_name].append(parser(path) for path in paths)
for model_name, (lookup, path) in models_lists:
- models_dict[model_name].append(iter_json_file(path, lookup))
+ models_dict[model_name].append(iter_json_file(parser(path), lookup))
self.models_data = {
model_name: itertools.chain(*list_of_gen)
@@ -252,6 +266,12 @@ def _create_argparser(cls) -> argparse.ArgumentParser:
"I.e. for file that contains dict {\"a\": {\"b\": [model_data, ...]}} you should\n"
"pass 'a.b' as .\n\n"
)
+ parser.add_argument(
+ "-i", "--input-format",
+ metavar="FORMAT", default="json",
+ choices=['json', 'yaml', 'ini'],
+ help="Input files parser ('PyYaml' is required to parse yaml files)\n\n"
+ )
parser.add_argument(
"-o", "--output",
metavar="FILE", default="",
@@ -385,7 +405,31 @@ def path_split(path: str) -> List[str]:
return folders
-def dict_lookup(d: dict, lookup: str) -> Union[dict, list]:
+class FileLoaders:
+ T = Callable[[Path], Union[dict, list]]
+
+ @staticmethod
+ def json(path: Path) -> Union[dict, list]:
+ with path.open() as fp:
+ return json.load(fp)
+
+ @staticmethod
+ def yaml(path: Path) -> Union[dict, list]:
+ if yaml is None:
+ print('Yaml parser is not installed. To parse yaml files PyYaml (or ruamel.yaml) is required.')
+ raise ImportError('yaml')
+ with path.open() as fp:
+ return yaml.safe_load(fp)
+
+ @staticmethod
+ def ini(path: Path) -> dict:
+ config = configparser.ConfigParser()
+ with path.open() as fp:
+ config.read_file(fp)
+ return {s: dict(config.items(s)) for s in config.sections()}
+
+
+def dict_lookup(d: Union[dict, list], lookup: str) -> Union[dict, list]:
"""
Extract nested dictionary value from key path.
If lookup is "-" returns dict as is.
@@ -403,7 +447,7 @@ def dict_lookup(d: dict, lookup: str) -> Union[dict, list]:
return d
-def iter_json_file(path: Path, lookup: str) -> Generator[Union[dict, list], Any, None]:
+def iter_json_file(data: Union[dict, list], lookup: str) -> Generator[Union[dict, list], Any, None]:
"""
Loads given 'path' file, perform lookup and return generator over json list.
Does not open file until iteration is started.
@@ -412,21 +456,11 @@ def iter_json_file(path: Path, lookup: str) -> Generator[Union[dict, list], Any,
:param lookup: Dot separated lookup path
:return:
"""
- with path.open() as f:
- l = json.load(f)
- l = dict_lookup(l, lookup)
+ l = dict_lookup(data, lookup)
assert isinstance(l, list), f"Dict lookup return {type(l)} but list is expected, check your lookup path"
yield from l
-def safe_json_load(path: Path) -> Union[dict, list]:
- """
- Open file, load json and close it.
- """
- with path.open(encoding="utf-8") as f:
- return json.load(f)
-
-
def _process_path(path: str) -> Iterable[Path]:
"""
Convert path pattern into path iterable.
diff --git a/json_to_models/models/base.py b/json_to_models/models/base.py
index 17ce86e..80c82ea 100644
--- a/json_to_models/models/base.py
+++ b/json_to_models/models/base.py
@@ -23,7 +23,7 @@
keywords_set = set(keyword.kwlist)
builtins_set = set(__builtins__.keys())
-other_common_names_set = {'datetime', 'time', 'date', 'defaultdict'}
+other_common_names_set = {'datetime', 'time', 'date', 'defaultdict', 'schema'}
blacklist_words = frozenset(keywords_set | builtins_set | other_common_names_set)
ones = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
diff --git a/setup.py b/setup.py
index 1710210..af89a0d 100644
--- a/setup.py
+++ b/setup.py
@@ -50,6 +50,6 @@ def run_tests(self):
},
install_requires=required,
cmdclass={"test": PyTest},
- tests_require=["pytest>=4.4.0", "pytest-xdist", "requests", "attrs", "pydantic>=1.3"],
+ tests_require=["pytest>=4.4.0", "pytest-xdist", "requests", "attrs", "pydantic>=1.3", "PyYaml"],
data_files=[('', ['requirements.txt', 'pytest.ini', '.coveragerc', 'LICENSE', 'README.md', 'CHANGELOG.md'])]
)
diff --git a/test/test_cli/data/file.ini b/test/test_cli/data/file.ini
new file mode 100644
index 0000000..fb52254
--- /dev/null
+++ b/test/test_cli/data/file.ini
@@ -0,0 +1,9 @@
+[owner]
+name = John Doe
+organization = Acme Widgets Inc.
+
+[database]
+; use IP address in case network name resolution is not working
+server = 192.0.2.62
+port = 143
+file = payroll.dat
diff --git a/test/test_cli/data/spotify-swagger.yaml b/test/test_cli/data/spotify-swagger.yaml
new file mode 100644
index 0000000..84a0b0d
--- /dev/null
+++ b/test/test_cli/data/spotify-swagger.yaml
@@ -0,0 +1,2004 @@
+swagger: '2.0'
+schemes:
+ - https
+host: api.spotify.com
+basePath: /v1
+info:
+ title: Spotify
+ version: v1
+ x-apisguru-categories:
+ - media
+ x-origin:
+ - format: raml
+ url: 'https://raw.githubusercontent.com/spotify/web-api/master/specifications/raml/api.raml'
+ version: '0.8'
+ x-providerName: spotify.com
+ x-logo:
+ url: https://logo-core.clearbit.com/spotify.com
+externalDocs:
+ url: 'https://developer.spotify.com/'
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ oauth_2_0_accessCode:
+ authorizationUrl: 'https://accounts.spotify.com/authorize'
+ description: |
+ Spotify supports OAuth 2.0 for authenticating all API requests.
+ flow: accessCode
+ scopes:
+ playlist-modify-private: ''
+ playlist-modify-public: ''
+ playlist-read-collaborative: ''
+ playlist-read-private: ''
+ user-follow-modify: ''
+ user-follow-read: ''
+ user-library-modify: ''
+ user-library-read: ''
+ user-read-birthdate: ''
+ user-read-email: ''
+ user-read-private: ''
+ tokenUrl: 'https://accounts.spotify.com/api/token'
+ type: oauth2
+ oauth_2_0_implicit:
+ authorizationUrl: 'https://accounts.spotify.com/authorize'
+ description: |
+ Spotify supports OAuth 2.0 for authenticating all API requests.
+ flow: implicit
+ scopes:
+ playlist-modify-private: ''
+ playlist-modify-public: ''
+ playlist-read-collaborative: ''
+ playlist-read-private: ''
+ user-follow-modify: ''
+ user-follow-read: ''
+ user-library-modify: ''
+ user-library-read: ''
+ user-read-birthdate: ''
+ user-read-email: ''
+ user-read-private: ''
+ type: oauth2
+paths:
+ /albums:
+ get:
+ description: |
+ [Get Several Albums](https://developer.spotify.com/web-api/get-several-albums/)
+ parameters:
+ - description: A comma-separated list of IDs
+ in: query
+ name: ids
+ required: true
+ type: string
+ - description: The market (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: market
+ required: false
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ properties:
+ albums:
+ items:
+ $ref: '#/definitions/album'
+ type: array
+ type: object
+ '/albums/{id}':
+ get:
+ description: |
+ [Get an Album](https://developer.spotify.com/web-api/get-album/)
+ parameters:
+ - description: The Spotify ID for the album
+ in: path
+ name: id
+ required: true
+ type: string
+ - description: The market (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: market
+ required: false
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/album'
+ '/albums/{id}/tracks':
+ get:
+ description: |
+ [Get an Album's Tracks](https://developer.spotify.com/web-api/get-albums-tracks/)
+ parameters:
+ - description: The Spotify ID for the album
+ in: path
+ name: id
+ required: true
+ type: string
+ - default: 20
+ description: The maximum number of items to return
+ in: query
+ maximum: 50
+ minimum: 0
+ name: limit
+ required: false
+ type: integer
+ - default: 0
+ description: The index of the first item to return
+ in: query
+ name: offset
+ required: false
+ type: integer
+ - description: The market (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: market
+ required: false
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/track-simple-page'
+ /artists:
+ get:
+ description: |
+ [Get Several Artists](https://developer.spotify.com/web-api/get-several-artists/)
+ parameters:
+ - description: A comma-separated list of IDs
+ in: query
+ name: ids
+ required: true
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ properties:
+ artists:
+ items:
+ $ref: '#/definitions/artist'
+ type: array
+ type: object
+ '/artists/{id}':
+ get:
+ description: |
+ [Get an Artist](https://developer.spotify.com/web-api/get-artist/)
+ parameters:
+ - description: The Spotify ID for the artist
+ in: path
+ name: id
+ required: true
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/artist'
+ '/artists/{id}/albums':
+ get:
+ description: |
+ [Get an Artist's Albums](https://developer.spotify.com/web-api/get-artists-albums/)
+ parameters:
+ - description: The Spotify ID for the artist
+ in: path
+ name: id
+ required: true
+ type: string
+ - default: 20
+ description: The maximum number of items to return
+ in: query
+ maximum: 50
+ minimum: 0
+ name: limit
+ required: false
+ type: integer
+ - default: 0
+ description: The index of the first item to return
+ in: query
+ name: offset
+ required: false
+ type: integer
+ - description: Filter by album types
+ in: query
+ name: album_type
+ required: false
+ type: string
+ - description: The market (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: market
+ required: false
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/album-simple-page'
+ '/artists/{id}/related-artists':
+ get:
+ description: |
+ [Get an Artist's Related Artists](https://developer.spotify.com/web-api/get-related-artists/)
+ parameters:
+ - description: The Spotify ID for the artist
+ in: path
+ name: id
+ required: true
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ properties:
+ artists:
+ items:
+ $ref: '#/definitions/artist'
+ type: array
+ type: object
+ '/artists/{id}/top-tracks':
+ get:
+ description: |
+ [Get an Artist's Top Tracks](https://developer.spotify.com/web-api/get-artists-top-tracks/)
+ parameters:
+ - description: The Spotify ID for the artist
+ in: path
+ name: id
+ required: true
+ type: string
+ - description: The country (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: country
+ required: true
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ properties:
+ tracks:
+ items:
+ $ref: '#/definitions/track'
+ type: array
+ type: object
+ /browse/categories:
+ get:
+ description: |
+ [Get a List of Browse Categories](https://developer.spotify.com/web-api/get-list-categories/)
+ parameters:
+ - default: 20
+ description: The maximum number of items to return
+ in: query
+ maximum: 50
+ minimum: 0
+ name: limit
+ required: false
+ type: integer
+ - default: 0
+ description: The index of the first item to return
+ in: query
+ name: offset
+ required: false
+ type: integer
+ - description: The country (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: country
+ required: false
+ type: string
+ - description: |
+ The desired language, consisting of an ISO 639 language code and an ISO 3166-1 alpha-2 country code, joined by an underscore. For example: es_MX, meaning "Spanish (Mexico)".
+ in: query
+ name: locale
+ required: false
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/category-page'
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ '/browse/categories/{category_id}':
+ get:
+ description: |
+ [Get a Single Browse Category](https://developer.spotify.com/web-api/get-category/)
+ parameters:
+ - description: The Spotify ID of the category you wish to fetch.
+ in: path
+ name: category_id
+ required: true
+ type: string
+ - description: The country (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: country
+ required: false
+ type: string
+ - description: |
+ The desired language, consisting of an ISO 639 language code and an ISO 3166-1 alpha-2 country code, joined by an underscore. For example: es_MX, meaning "Spanish (Mexico)".
+ in: query
+ name: locale
+ required: false
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/category'
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ '/browse/categories/{category_id}/playlists':
+ get:
+ description: |
+ [Get a Category's playlists](https://developer.spotify.com/web-api/get-categorys-playlists/)
+ parameters:
+ - description: The Spotify ID of the category you wish to fetch.
+ in: path
+ name: category_id
+ required: true
+ type: string
+ - default: 20
+ description: The maximum number of items to return
+ in: query
+ maximum: 50
+ minimum: 0
+ name: limit
+ required: false
+ type: integer
+ - default: 0
+ description: The index of the first item to return
+ in: query
+ name: offset
+ required: false
+ type: integer
+ - description: The country (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: country
+ required: false
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ properties:
+ playlists:
+ $ref: '#/definitions/playlist-simple-page'
+ type: object
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ /browse/featured-playlists:
+ get:
+ description: |
+ [Get a List of Featured Playlists](https://developer.spotify.com/web-api/get-list-featured-playlists/)
+ parameters:
+ - default: 20
+ description: The maximum number of items to return
+ in: query
+ maximum: 50
+ minimum: 0
+ name: limit
+ required: false
+ type: integer
+ - default: 0
+ description: The index of the first item to return
+ in: query
+ name: offset
+ required: false
+ type: integer
+ - description: The country (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: country
+ required: false
+ type: string
+ - description: |
+ The desired language, consisting of an ISO 639 language code and an ISO 3166-1 alpha-2 country code, joined by an underscore. For example: es_MX, meaning "Spanish (Mexico)".
+ in: query
+ name: locale
+ required: false
+ type: string
+ - description: |
+ A timestamp in ISO 8601 format (yyyy-MM-dd'T'HH:mm:ss) with the user's local time to get results tailored to a specific date and time in the day. If not provided, it defaults to the current UTC time. Example: "2014-10-23T09:00:00" for a user whose local time is 9AM.
+ in: query
+ name: timestamp
+ required: false
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/featured-playlists'
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ /browse/new-releases:
+ get:
+ description: |
+ [Get a List of New Releases](https://developer.spotify.com/web-api/get-list-new-releases/)
+ parameters:
+ - default: 20
+ description: The maximum number of items to return
+ in: query
+ maximum: 50
+ minimum: 0
+ name: limit
+ required: false
+ type: integer
+ - default: 0
+ description: The index of the first item to return
+ in: query
+ name: offset
+ required: false
+ type: integer
+ - description: The country (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: country
+ required: false
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/album-simple-page'
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ /me:
+ get:
+ description: |
+ [Get Current User's Profile](https://developer.spotify.com/web-api/get-current-users-profile/)
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/current-user-profile'
+ /me/following:
+ delete:
+ description: |
+ [Unfollow Artists or Users](https://developer.spotify.com/web-api/unfollow-artists-users/)
+ parameters:
+ - description: The type to unfollow.
+ enum:
+ - artist
+ - user
+ in: query
+ name: type
+ required: true
+ type: string
+ - description: A comma-separated list of the artists or users ids
+ in: query
+ name: ids
+ required: true
+ type: string
+ responses:
+ '204':
+ description: No Content
+ get:
+ description: |
+ [Get User's Followed Artists](https://developer.spotify.com/web-api/get-followed-artists/)
+ parameters:
+ - description: 'The ID type, currently only artist is supported.'
+ enum:
+ - artist
+ in: query
+ name: type
+ required: true
+ type: string
+ - default: 20
+ description: The maximum number of items to return
+ in: query
+ maximum: 50
+ minimum: 0
+ name: limit
+ required: false
+ type: integer
+ - description: The last artist ID retrieved from the previous request.
+ in: query
+ name: after
+ required: false
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/user-followed'
+ put:
+ description: |
+ [Follow Artists or Users](https://developer.spotify.com/web-api/follow-artists-users/)
+ parameters:
+ - description: The type to follow.
+ enum:
+ - artist
+ - user
+ in: query
+ name: type
+ required: true
+ type: string
+ - description: A comma-separated list of the artists or users ids
+ in: query
+ name: ids
+ required: true
+ type: string
+ responses:
+ '204':
+ description: No Content
+ /me/following/contains:
+ get:
+ description: |
+ [Check if Current User Follows Artists or Users](https://developer.spotify.com/web-api/check-current-user-follows/)
+ parameters:
+ - description: The type to follow.
+ enum:
+ - artist
+ - user
+ in: query
+ name: type
+ required: true
+ type: string
+ - description: A comma-separated string of the artists or users ids.
+ in: query
+ name: ids
+ required: true
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ items:
+ description: 'True if the user follows the artist/user, false otherwise'
+ type: boolean
+ type: array
+ /me/tracks:
+ delete:
+ description: |
+ [Remove Tracks for Current User](https://developer.spotify.com/web-api/remove-tracks-user/)
+ parameters:
+ - description: A comma-separated list of IDs
+ in: query
+ name: ids
+ required: true
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ produces:
+ - text/plain
+ responses:
+ '200':
+ description: OK
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ get:
+ description: |
+ [Get Current User's Saved Tracks](https://developer.spotify.com/web-api/get-users-saved-tracks/)
+ parameters:
+ - default: 20
+ description: The maximum number of items to return
+ in: query
+ maximum: 50
+ minimum: 0
+ name: limit
+ required: false
+ type: integer
+ - default: 0
+ description: The index of the first item to return
+ in: query
+ name: offset
+ required: false
+ type: integer
+ - description: The market (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: market
+ required: false
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/saved-track-page'
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ put:
+ description: |
+ [Save Tracks for Current User](https://developer.spotify.com/web-api/save-tracks-user/)
+ parameters:
+ - description: A comma-separated list of IDs
+ in: query
+ name: ids
+ required: true
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ produces:
+ - text/plain
+ responses:
+ '200':
+ description: OK
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ /me/tracks/contains:
+ get:
+ description: |
+ [Check Current User's Saved Tracks](https://developer.spotify.com/web-api/check-users-saved-tracks/)
+ parameters:
+ - description: A comma-separated list of IDs
+ in: query
+ name: ids
+ required: true
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ items:
+ description: 'True if the track is in user''s Your Music library, false otherwise'
+ type: boolean
+ type: array
+ /search:
+ get:
+ description: |
+ [Search for an Item](https://developer.spotify.com/web-api/search-item/)
+ parameters:
+ - default: 20
+ description: The maximum number of items to return
+ in: query
+ maximum: 50
+ minimum: 0
+ name: limit
+ required: false
+ type: integer
+ - default: 0
+ description: The index of the first item to return
+ in: query
+ name: offset
+ required: false
+ type: integer
+ - description: 'The search query''s keywords (and optional field filters). The search is not case-sensitive: ''roadhouse'' will match ''Roadhouse'', ''roadHouse'', etc. Keywords will be matched in any order unless surrounded by quotes, thus q=roadhouse&20blues will match both ''Blues Roadhouse'' and ''Roadhouse of the Blues''. Quotation marks can be used to limit the match to a phrase: q=roadhouse&20blues will match ''My Roadhouse Blues'' but not ''Roadhouse of the Blues''. By default, results are returned when a match is found in any field of the target object type. Searches can be made more specific by specifying an album, artist or track field filter. For example q=album:gold%20artist:abba&type=album will search for albums with the text ''gold'' in the album name and the text ''abba'' in an artist name. Other possible field filters, depending on object types being searched, include year, genre, upc, and isrc. For example, q=damian%20genre:reggae-pop&type=artist. The asterisk (*) character can, with some limitations, be used as a wildcard (maximum: 2 per query). It will match a variable number of non-white-space characters. It cannot be used in a quoted phrase, in a field filter, or as the first character of the keyword string. Searching for playlists will return results matching the playlist''s name and/or description.'
+ in: query
+ name: q
+ required: true
+ type: string
+ - description: 'A comma-separated list of item types to search across. Search results will include hits from all the specified item types; for example q=name:abacab&type=album,track will return both albums and tracks with "abacab" in their name.'
+ in: query
+ name: type
+ required: true
+ type: string
+ - description: 'The market (an ISO 3166-1 alpha-2 country code). If given, only items with content playable in that market will be returned.'
+ in: query
+ name: market
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/search'
+ /tracks:
+ get:
+ description: |
+ [Get Several Tracks](https://developer.spotify.com/web-api/get-several-tracks/)
+ parameters:
+ - description: A comma-separated list of IDs
+ in: query
+ name: ids
+ required: true
+ type: string
+ - description: The market (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: market
+ required: false
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ properties:
+ tracks:
+ items:
+ $ref: '#/definitions/track'
+ type: array
+ type: object
+ '/tracks/{id}':
+ get:
+ description: |
+ [Get a Track](https://developer.spotify.com/web-api/get-track/)
+ parameters:
+ - in: path
+ name: id
+ required: true
+ type: string
+ - description: The market (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: market
+ required: false
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/track'
+ '/users/{user_id}':
+ get:
+ description: |
+ [Get a User's Profile](https://developer.spotify.com/web-api/get-users-profile/)
+ parameters:
+ - description: The user's Spotify user ID.
+ in: path
+ name: user_id
+ required: true
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/user-profile'
+ '/users/{user_id}/playlists':
+ get:
+ description: |
+ [Get a List of a User's Playlists](https://developer.spotify.com/web-api/get-list-users-playlists/)
+ parameters:
+ - description: The user's Spotify user ID.
+ in: path
+ name: user_id
+ required: true
+ type: string
+ - default: 20
+ description: The maximum number of items to return
+ in: query
+ maximum: 50
+ minimum: 0
+ name: limit
+ required: false
+ type: integer
+ - default: 0
+ description: The index of the first item to return
+ in: query
+ name: offset
+ required: false
+ type: integer
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/playlist-simple-page'
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ post:
+ description: |
+ [Create a Playlist](https://developer.spotify.com/web-api/create-playlist/)
+ parameters:
+ - description: The user's Spotify user ID.
+ in: path
+ name: user_id
+ required: true
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ - in: body
+ name: body
+ required: true
+ schema:
+ properties:
+ name:
+ type: string
+ public:
+ type: boolean
+ required:
+ - name
+ type: object
+ responses:
+ '201':
+ description: Created
+ schema:
+ $ref: '#/definitions/playlist'
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ '/users/{user_id}/playlists/{playlist_id}':
+ get:
+ description: |
+ [Get a Playlist](https://developer.spotify.com/web-api/get-playlist/)
+ parameters:
+ - description: The user's Spotify user ID.
+ in: path
+ name: user_id
+ required: true
+ type: string
+ - description: The Spotify playlist ID.
+ in: path
+ name: playlist_id
+ required: true
+ type: string
+ - description: A comma-separated list of fields to filter query
+ in: query
+ name: fields
+ type: string
+ - description: The market (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: market
+ required: false
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/playlist'
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ put:
+ description: |
+ [Change a Playlist's Details](https://developer.spotify.com/web-api/change-playlist-details/)
+ parameters:
+ - description: The user's Spotify user ID.
+ in: path
+ name: user_id
+ required: true
+ type: string
+ - description: The Spotify playlist ID.
+ in: path
+ name: playlist_id
+ required: true
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ - in: body
+ name: body
+ required: true
+ schema:
+ properties:
+ name:
+ type: string
+ public:
+ type: boolean
+ type: object
+ produces:
+ - text/plain
+ responses:
+ '200':
+ description: OK
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ '/users/{user_id}/playlists/{playlist_id}/followers':
+ delete:
+ description: |
+ [Unfollow a Playlist](https://developer.spotify.com/web-api/unfollow-playlist/)
+ parameters:
+ - description: The user's Spotify user ID.
+ in: path
+ name: user_id
+ required: true
+ type: string
+ - description: The Spotify playlist ID.
+ in: path
+ name: playlist_id
+ required: true
+ type: string
+ produces:
+ - text/plain
+ responses:
+ '200':
+ description: OK
+ put:
+ description: |
+ [Follow a Playlist](https://developer.spotify.com/web-api/follow-playlist/)
+ parameters:
+ - description: The user's Spotify user ID.
+ in: path
+ name: user_id
+ required: true
+ type: string
+ - description: The Spotify playlist ID.
+ in: path
+ name: playlist_id
+ required: true
+ type: string
+ - in: body
+ name: body
+ required: true
+ schema:
+ properties:
+ public:
+ type: boolean
+ type: object
+ produces:
+ - text/plain
+ responses:
+ '200':
+ description: OK
+ '/users/{user_id}/playlists/{playlist_id}/followers/contains':
+ get:
+ description: |
+ [Check if Users Follow a Playlist](https://developer.spotify.com/web-api/check-user-following-playlist/)
+ parameters:
+ - description: The user's Spotify user ID.
+ in: path
+ name: user_id
+ required: true
+ type: string
+ - description: The Spotify playlist ID.
+ in: path
+ name: playlist_id
+ required: true
+ type: string
+ - description: A comma-separated list of users ids
+ in: query
+ name: ids
+ required: true
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ items:
+ description: 'True if the user follows the playlist, false otherwise'
+ type: boolean
+ type: array
+ '/users/{user_id}/playlists/{playlist_id}/tracks':
+ delete:
+ description: |
+ [Remove Tracks from a Playlist](https://developer.spotify.com/web-api/remove-tracks-playlist/)
+ parameters:
+ - description: The user's Spotify user ID.
+ in: path
+ name: user_id
+ required: true
+ type: string
+ - description: The Spotify playlist ID.
+ in: path
+ name: playlist_id
+ required: true
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ - in: body
+ name: body
+ required: true
+ schema:
+ properties:
+ tracks:
+ items: { }
+ type: array
+ required:
+ - tracks
+ type: object
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/playlist-snapshot'
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ get:
+ description: |
+ [Get a Playlist's Tracks](https://developer.spotify.com/web-api/get-playlists-tracks/)
+ parameters:
+ - description: The user's Spotify user ID.
+ in: path
+ name: user_id
+ required: true
+ type: string
+ - description: The Spotify playlist ID.
+ in: path
+ name: playlist_id
+ required: true
+ type: string
+ - default: 20
+ description: The maximum number of items to return
+ in: query
+ maximum: 50
+ minimum: 0
+ name: limit
+ required: false
+ type: integer
+ - default: 0
+ description: The index of the first item to return
+ in: query
+ name: offset
+ required: false
+ type: integer
+ - description: A comma-separated list of fields to filter query
+ in: query
+ name: fields
+ type: string
+ - description: The market (an ISO 3166-1 alpha-2 country code)
+ in: query
+ name: market
+ required: false
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/playlist-track-page'
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ post:
+ description: |
+ [Add Tracks to a Playlist](https://developer.spotify.com/web-api/add-tracks-to-playlist/)
+ parameters:
+ - description: The user's Spotify user ID.
+ in: path
+ name: user_id
+ required: true
+ type: string
+ - description: The Spotify playlist ID.
+ in: path
+ name: playlist_id
+ required: true
+ type: string
+ - description: 'The position to insert the tracks, a zero-based index'
+ in: query
+ name: position
+ required: false
+ type: integer
+ - description: A comma-separated list of Spotify track URIs to add. A maximum of 100 tracks can be added in one request.
+ in: query
+ name: uris
+ required: true
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/playlist-snapshot'
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+ put:
+ description: |
+ [Reorder or replace a Playlist's Tracks](https://developer.spotify.com/web-api/reorder-playlists-tracks/)
+ parameters:
+ - description: The user's Spotify user ID.
+ in: path
+ name: user_id
+ required: true
+ type: string
+ - description: The Spotify playlist ID.
+ in: path
+ name: playlist_id
+ required: true
+ type: string
+ - description: It is used to set specified media type.
+ in: header
+ name: Accept
+ type: string
+ - in: body
+ name: body
+ required: true
+ schema:
+ properties:
+ uris:
+ items: { }
+ type: array
+ required:
+ - uris
+ type: object
+ responses:
+ '200':
+ description: OK
+ schema:
+ $ref: '#/definitions/playlist-snapshot'
+ '429':
+ description: |
+ API rate limit exceeded. See http://developer.spotify.com/web-api/user-guide/#rate-limiting for details.
+definitions:
+ album:
+ properties:
+ album_type:
+ description: 'The type of the album: one of ''album'', ''single'', or ''compilation''.'
+ type: string
+ artists:
+ description: The artists of the album. Each artist object includes a link in href to more detailed information about the artist.
+ items:
+ $ref: '#/definitions/artist'
+ type: array
+ available_markets:
+ description: 'The markets in which the album is available: ISO 3166-1 alpha-2 country codes. Note that an album is considered available in a market when at least 1 of its tracks is available in that market.'
+ items:
+ type: string
+ type: array
+ copyrights:
+ description: The copyright statements of the album.
+ items:
+ properties:
+ text:
+ description: The copyright text for this album.
+ type: string
+ type:
+ description: 'The type of copyright: C = the copyright, P = the sound recording (performance) copyright.'
+ type: string
+ type: object
+ type: array
+ external_ids:
+ additionalProperties:
+ description: 'The identifier type, for example: ''isrc'' - International Standard Recording Code, ''ean'' - International Article Number, ''upc'' - Universal Product Code'
+ type: string
+ description: Known external IDs for this album.
+ type: object
+ external_urls:
+ additionalProperties:
+ description: 'The type of the URL, for example: ''spotify'' - The Spotify URL for the object.'
+ type: string
+ description: Known external URLs for this album.
+ type: object
+ genres:
+ description: 'A list of the genres used to classify the album. For example: ''Prog Rock'', ''Post-Grunge''. (If not yet classified, the array is empty.)'
+ items:
+ type: string
+ type: array
+ href:
+ description: A link to the Web API endpoint providing full details of the album.
+ type: string
+ id:
+ description: The Spotify ID for the album.
+ type: string
+ images:
+ description: 'The cover art for the album in various sizes, widest first.'
+ items:
+ $ref: '#/definitions/image'
+ type: array
+ name:
+ description: The name of the album.
+ type: string
+ popularity:
+ description: 'The popularity of the album. The value will be between 0 and 100, with 100 being the most popular. The popularity is calculated from the popularity of the album''s individual tracks.'
+ type: integer
+ release_date:
+ description: 'The date the album was first released, for example ''1981-12-15''. Depending on the precision, it might be shown as ''1981'' or ''1981-12''.'
+ type: string
+ release_date_precision:
+ description: 'The precision with which release_date value is known: ''year'', ''month'', or ''day''.'
+ type: string
+ tracks:
+ $ref: '#/definitions/track-simple-page'
+ type:
+ description: 'The object type: ''album''.'
+ type: string
+ uri:
+ description: The Spotify URI for the album.
+ type: string
+ type: object
+ album-simple:
+ properties:
+ album_type:
+ description: 'The type of the album: one of ''album'', ''single'', or ''compilation''.'
+ type: string
+ available_markets:
+ description: 'The markets in which the album is available: ISO 3166-1 alpha-2 country codes. Note that an album is considered available in a market when at least 1 of its tracks is available in that market.'
+ items:
+ type: string
+ type: array
+ external_urls:
+ additionalProperties:
+ description: 'The type of the URL, for example: ''spotify'' - The Spotify URL for the object.'
+ type: string
+ description: Known external URLs for this album.
+ type: object
+ href:
+ description: A link to the Web API endpoint providing full details of the album.
+ type: string
+ id:
+ description: The Spotify ID for the album.
+ type: string
+ images:
+ description: 'The cover art for the album in various sizes, widest first.'
+ items:
+ $ref: '#/definitions/image'
+ type: array
+ name:
+ description: The name of the album.
+ type: string
+ type:
+ description: 'The object type: ''album''.'
+ type: string
+ uri:
+ description: The Spotify URI for the album.
+ type: string
+ type: object
+ album-simple-page:
+ properties:
+ href:
+ description: A link to the Web API endpoint returning the full result of the request.
+ type: string
+ items:
+ description: The requested data.
+ items:
+ $ref: '#/definitions/album-simple'
+ type: array
+ limit:
+ description: The maximum number of items in the response (as set in the query or by default).
+ type: integer
+ next:
+ description: URL to the next page of items. (null if none)
+ type: string
+ offset:
+ description: The offset of the items returned (as set in the query or by default).
+ type: integer
+ previous:
+ description: URL to the previous page of items. (null if none)
+ type: string
+ total:
+ description: The total number of items available to return.
+ type: integer
+ type: object
+ album-track-page:
+ properties:
+ href:
+ description: A link to the Web API endpoint returning the full result of the request.
+ type: string
+ items:
+ description: The requested data.
+ items:
+ $ref: '#/definitions/track-simple'
+ type: array
+ limit:
+ description: The maximum number of items in the response (as set in the query or by default).
+ type: integer
+ next:
+ description: URL to the next page of items. (null if none)
+ type: string
+ offset:
+ description: The offset of the items returned (as set in the query or by default).
+ type: integer
+ previous:
+ description: URL to the previous page of items. (null if none)
+ type: string
+ total:
+ description: The total number of items available to return.
+ type: integer
+ type: object
+ artist:
+ properties:
+ external_urls:
+ additionalProperties:
+ type: string
+ description: Known external URLs for this artist.
+ type: object
+ followers:
+ $ref: '#/definitions/followers'
+ genres:
+ description: 'A list of the genres the artist is associated with. For example: ''Prog Rock'', ''Post-Grunge''. (If not yet classified, the array is empty.)'
+ items:
+ type: string
+ type: array
+ href:
+ description: A link to the Web API endpoint providing full details of the artist.
+ type: string
+ id:
+ description: The Spotify ID for the artist.
+ type: string
+ images:
+ description: 'Images of the artist in various sizes, widest first.'
+ items:
+ $ref: '#/definitions/image'
+ type: array
+ name:
+ description: The name of the artist.
+ type: string
+ popularity:
+ description: 'The popularity of the artist. The value will be between 0 and 100, with 100 being the most popular. The artist''s popularity is calculated from the popularity of all the artist''s tracks.'
+ type: integer
+ type:
+ description: 'The object type: ''artist'''
+ type: string
+ uri:
+ description: The Spotify URI for the artist.
+ type: string
+ type: object
+ artist-simple:
+ properties:
+ external_urls:
+ additionalProperties:
+ type: string
+ description: Known external URLs for this artist.
+ type: object
+ href:
+ description: A link to the Web API endpoint providing full details of the artist.
+ type: string
+ id:
+ description: The Spotify ID for the artist.
+ type: string
+ name:
+ description: The name of the artist.
+ type: string
+ type:
+ description: 'The object type: ''artist'''
+ type: string
+ uri:
+ description: The Spotify URI for the artist.
+ type: string
+ type: object
+ category:
+ properties:
+ href:
+ description: A link to the Web API endpoint returning full details of the category.
+ type: string
+ icons:
+ items:
+ $ref: '#/definitions/image'
+ type: array
+ id:
+ description: The Spotify category ID of the category.
+ type: string
+ name:
+ description: The name of the category.
+ type: string
+ type: object
+ category-page:
+ properties:
+ href:
+ description: A link to the Web API endpoint returning the full result of the request.
+ type: string
+ items:
+ description: The requested data.
+ items:
+ $ref: '#/definitions/category'
+ type: array
+ limit:
+ description: The maximum number of items in the response (as set in the query or by default).
+ type: integer
+ next:
+ description: URL to the next page of items. (null if none)
+ type: string
+ offset:
+ description: The offset of the items returned (as set in the query or by default).
+ type: integer
+ previous:
+ description: URL to the previous page of items. (null if none)
+ type: string
+ total:
+ description: The total number of items available to return.
+ type: integer
+ type: object
+ current-user-profile:
+ properties:
+ birthdate:
+ description: The user's date-of-birth. This field is only available when the current user has granted access to the user-read-birthdate scope.
+ type: string
+ country:
+ description: 'The country of the user, as set in the user''s account profile. An ISO 3166-1 alpha-2 country code. This field is only available when the current user has granted access to the user-read-private scope.'
+ type: string
+ displayName:
+ description: The name displayed on the user's profile.
+ type: string
+ email:
+ description: 'The user''s email address, as entered by the user when creating their account. This email address is unverified; there is no proof that it actually belongs to the user. This field is only available when the current user has granted access to the user-read-email scope.'
+ type: string
+ external_urls:
+ additionalProperties:
+ type: string
+ description: Known external URLs for this user.
+ type: object
+ followers:
+ $ref: '#/definitions/followers'
+ href:
+ description: A link to the Web API endpoint for this user.
+ type: string
+ id:
+ description: The Spotify ID for this user.
+ type: string
+ product:
+ description: 'The user''s Spotify subscription level: ''premium'', ''free'', etc. (The subscription level ''open'' can be considered the same as ''free''.) This field is only available when the current user has granted access to the user-read-private scope.'
+ type: string
+ type:
+ description: 'The object type: ''user'''
+ type: string
+ uri:
+ description: The Spotify URI for the user.
+ type: string
+ type: object
+ featured-playlists:
+ properties:
+ message:
+ description: A sentence describing the context for the featured playlists.
+ type: string
+ playlists:
+ $ref: '#/definitions/playlist-simple-page'
+ type: object
+ followers:
+ description: Information about the followers of the artist.
+ properties:
+ href:
+ description: A link to the Web API endpoint providing full details of the followers; null if not available.
+ type: string
+ total:
+ description: The total number of followers.
+ type: integer
+ type: object
+ image:
+ properties:
+ height:
+ description: 'The image height in pixels. If unknown: null or not returned.'
+ type: integer
+ url:
+ description: The source URL of the image.
+ type: string
+ width:
+ description: 'The image width in pixels. If unknown: null or not returned.'
+ type: integer
+ type: object
+ playlist:
+ properties:
+ collaborative:
+ description: True if the owner allows other users to modify the playlist.
+ type: boolean
+ description:
+ description: 'The playlist description. Only returned for modified, verified playlists, otherwise null.'
+ type: string
+ external_urls:
+ additionalProperties:
+ description: 'The type of the URL, for example: ''spotify'' - The Spotify URL for the object.'
+ type: string
+ description: Known external URLs for this album.
+ type: object
+ followers:
+ description: Information about the followers of the playlist.
+ properties:
+ href:
+ description: A link to the Web API endpoint providing full details of the followers; null if not available.
+ type: string
+ total:
+ description: The total number of followers.
+ type: integer
+ type: object
+ href:
+ description: A link to the Web API endpoint providing full details of the playlist.
+ type: string
+ id:
+ description: The Spotify ID of the playlist.
+ type: string
+ images:
+ description: 'The cover art for the album in various sizes, widest first.'
+ items:
+ $ref: '#/definitions/image'
+ type: array
+ name:
+ description: The name of the playlist.
+ type: string
+ owner:
+ $ref: '#/definitions/user-profile'
+ public:
+ description: 'The playlist''s public/private status: true the playlist is public, false the playlist is private, null the playlist status is not relevant. For more about public/private status, see Working with Playlists.'
+ type: boolean
+ snapshot_id:
+ description: The version identifier for the current playlist. Can be supplied in other requests to target a specific playlist version
+ type: string
+ tracks:
+ $ref: '#/definitions/playlist-track-page'
+ type:
+ description: 'The object type: ''playlist''.'
+ type: string
+ uri:
+ description: Spotify URI of the playlist.
+ type: string
+ type: object
+ playlist-simple:
+ properties:
+ collaborative:
+ description: True if the owner allows other users to modify the playlist.
+ type: boolean
+ external_urls:
+ additionalProperties:
+ description: 'The type of the URL, for example: ''spotify'' - The Spotify URL for the object.'
+ type: string
+ description: Known external URLs for this album.
+ type: object
+ href:
+ description: A link to the Web API endpoint providing full details of the playlist.
+ type: string
+ id:
+ description: The Spotify ID of the playlist.
+ type: string
+ images:
+ description: 'The cover art for the album in various sizes, widest first.'
+ items:
+ $ref: '#/definitions/image'
+ type: array
+ name:
+ description: The name of the playlist.
+ type: string
+ owner:
+ $ref: '#/definitions/user-profile'
+ public:
+ description: 'The playlist''s public/private status: true the playlist is public, false the playlist is private, null the playlist status is not relevant. For more about public/private status, see Working with Playlists.'
+ type: boolean
+ snapshot_id:
+ description: The version identifier for the current playlist. Can be supplied in other requests to target a specific playlist version
+ type: string
+ tracks:
+ properties:
+ href:
+ description: A link to the Web API endpoint returning the full result of the request.
+ type: string
+ total:
+ description: The total number of tracks available to return.
+ type: integer
+ type: object
+ type:
+ description: 'The object type: ''playlist''.'
+ type: string
+ uri:
+ description: Spotify URI of the playlist.
+ type: string
+ type: object
+ playlist-simple-page:
+ properties:
+ href:
+ description: A link to the Web API endpoint returning the full result of the request.
+ type: string
+ items:
+ description: The requested data.
+ items:
+ $ref: '#/definitions/playlist-simple'
+ type: array
+ limit:
+ description: The maximum number of items in the response (as set in the query or by default).
+ type: integer
+ next:
+ description: URL to the next page of items. (null if none)
+ type: string
+ offset:
+ description: The offset of the items returned (as set in the query or by default).
+ type: integer
+ previous:
+ description: URL to the previous page of items. (null if none)
+ type: string
+ total:
+ description: The total number of items available to return.
+ type: integer
+ type: object
+ playlist-snapshot:
+ properties:
+ snapshot_id:
+ description: The version identifier for the modified playlist. Can be supplied in other requests to target a specific playlist version.
+ type: string
+ type: object
+ playlist-track:
+ properties:
+ added_at:
+ description: The date and time the track was added in ISO 8601 format. Note that some very old playlists may return null in this field.
+ type: string
+ added_by:
+ $ref: '#/definitions/user-profile'
+ is_local:
+ description: 'Whether this track is a [local file](https://developer.spotify.com/web-api/local-files-spotify-playlists/) or not.'
+ type: boolean
+ track:
+ $ref: '#/definitions/track'
+ type: object
+ playlist-track-page:
+ properties:
+ href:
+ description: A link to the Web API endpoint returning the full result of the request.
+ type: string
+ items:
+ description: The requested data.
+ items:
+ $ref: '#/definitions/playlist-track'
+ type: array
+ limit:
+ description: The maximum number of items in the response (as set in the query or by default).
+ type: integer
+ next:
+ description: URL to the next page of items. (null if none)
+ type: string
+ offset:
+ description: The offset of the items returned (as set in the query or by default).
+ type: integer
+ previous:
+ description: URL to the previous page of items. (null if none)
+ type: string
+ total:
+ description: The total number of items available to return.
+ type: integer
+ type: object
+ saved-track:
+ properties:
+ added_at:
+ description: The date and time the track was added in ISO 8601 format. Note that some very old playlists may return null in this field.
+ type: string
+ track:
+ $ref: '#/definitions/track'
+ type: object
+ saved-track-page:
+ properties:
+ href:
+ description: A link to the Web API endpoint returning the full result of the request.
+ type: string
+ items:
+ description: The requested data.
+ items:
+ $ref: '#/definitions/saved-track'
+ type: array
+ limit:
+ description: The maximum number of items in the response (as set in the query or by default).
+ type: integer
+ next:
+ description: URL to the next page of items. (null if none)
+ type: string
+ offset:
+ description: The offset of the items returned (as set in the query or by default).
+ type: integer
+ previous:
+ description: URL to the previous page of items. (null if none)
+ type: string
+ total:
+ description: The total number of items available to return.
+ type: integer
+ type: object
+ search:
+ properties:
+ albums:
+ description: Present if the type of search includes 'album'.
+ properties:
+ href:
+ description: A link to the Web API endpoint returning the full result of the request.
+ type: string
+ items:
+ description: The requested data.
+ items:
+ $ref: '#/definitions/album-simple'
+ type: array
+ limit:
+ description: The maximum number of items in the response (as set in the query or by default).
+ type: integer
+ next:
+ description: URL to the next page of items. (null if none)
+ type: string
+ offset:
+ description: The offset of the items returned (as set in the query or by default).
+ type: integer
+ previous:
+ description: URL to the previous page of items. (null if none)
+ type: string
+ total:
+ description: The total number of items available to return.
+ type: integer
+ type: object
+ artists:
+ description: Present if the type of search includes 'artist'.
+ properties:
+ href:
+ description: A link to the Web API endpoint returning the full result of the request.
+ type: string
+ items:
+ description: The requested data.
+ items:
+ $ref: '#/definitions/artist'
+ type: array
+ limit:
+ description: The maximum number of items in the response (as set in the query or by default).
+ type: integer
+ next:
+ description: URL to the next page of items. (null if none)
+ type: string
+ offset:
+ description: The offset of the items returned (as set in the query or by default).
+ type: integer
+ previous:
+ description: URL to the previous page of items. (null if none)
+ type: string
+ total:
+ description: The total number of items available to return.
+ type: integer
+ type: object
+ tracks:
+ description: Present if the type of search includes 'track'.
+ properties:
+ href:
+ description: A link to the Web API endpoint returning the full result of the request.
+ type: string
+ items:
+ description: The requested data.
+ items:
+ $ref: '#/definitions/track'
+ type: array
+ limit:
+ description: The maximum number of items in the response (as set in the query or by default).
+ type: integer
+ next:
+ description: URL to the next page of items. (null if none)
+ type: string
+ offset:
+ description: The offset of the items returned (as set in the query or by default).
+ type: integer
+ previous:
+ description: URL to the previous page of items. (null if none)
+ type: string
+ total:
+ description: The total number of items available to return.
+ type: integer
+ type: object
+ type: object
+ track:
+ properties:
+ album:
+ $ref: '#/definitions/album-simple'
+ artists:
+ description: The artists who performed the track. Each artist object includes a link in href to more detailed information about the artist.
+ items:
+ $ref: '#/definitions/artist-simple'
+ type: array
+ available_markets:
+ description: 'A list of the countries in which the track can be played, identified by their ISO 3166-1 alpha-2 code. '
+ items:
+ type: string
+ type: array
+ disc_number:
+ description: The disc number (usually 1 unless the album consists of more than one disc).
+ type: integer
+ duration_ms:
+ description: The track length in milliseconds.
+ type: integer
+ explicit:
+ description: Whether or not the track has explicit lyrics (true = yes it does; false = no it does not OR unknown).
+ type: boolean
+ external_ids:
+ additionalProperties:
+ description: 'The identifier type, for example: ''isrc'' - International Standard Recording Code, ''ean'' - International Article Number, ''upc'' - Universal Product Code'
+ type: string
+ description: Known external IDs for the track.
+ type: object
+ external_urls:
+ additionalProperties:
+ description: 'The type of the URL, for example: ''spotify'' - The Spotify URL for the object.'
+ type: string
+ description: Known external URLs for this track.
+ type: object
+ href:
+ description: A link to the Web API endpoint providing full details of the track.
+ type: string
+ id:
+ description: The Spotify ID for the track.
+ type: string
+ is_playable:
+ description: 'Part of the response when Track Relinking is applied. If true, the track is playable in the given market. Otherwise false.'
+ type: boolean
+ linked_from:
+ description: 'Part of the response when Track Relinking is applied, and the requested track has been replaced with different track. The track in the linked_from object contains information about the originally requested track.'
+ properties:
+ external_urls:
+ additionalProperties:
+ description: 'The type of the URL, for example: ''spotify'' - The Spotify URL for the object.'
+ type: string
+ description: Known external URLs for this track.
+ type: object
+ href:
+ description: A link to the Web API endpoint providing full details of the track.
+ type: string
+ id:
+ description: The Spotify ID for the track.
+ type: string
+ type:
+ description: 'The object type: ''track''.'
+ type: string
+ uri:
+ description: The Spotify URI for the track.
+ type: string
+ type: object
+ name:
+ description: The name of the track.
+ type: string
+ preview_url:
+ description: A URL to a 30 second preview (MP3 format) of the track.
+ type: string
+ track_number:
+ description: 'The number of the track. If an album has several discs, the track number is the number on the specified disc.'
+ type: integer
+ type:
+ description: 'The object type: ''track''.'
+ type: string
+ uri:
+ description: The Spotify URI for the track.
+ type: string
+ type: object
+ track-simple:
+ properties:
+ artists:
+ description: The artists who performed the track. Each artist object includes a link in href to more detailed information about the artist.
+ items:
+ $ref: '#/definitions/artist-simple'
+ type: array
+ available_markets:
+ description: 'A list of the countries in which the track can be played, identified by their ISO 3166-1 alpha-2 code. '
+ items:
+ type: string
+ type: array
+ disc_number:
+ description: The disc number (usually 1 unless the album consists of more than one disc).
+ type: integer
+ duration_ms:
+ description: The track length in milliseconds.
+ type: integer
+ explicit:
+ description: Whether or not the track has explicit lyrics (true = yes it does; false = no it does not OR unknown).
+ type: boolean
+ external_urls:
+ additionalProperties:
+ description: 'The type of the URL, for example: ''spotify'' - The Spotify URL for the object.'
+ type: string
+ description: Known external URLs for this track.
+ type: object
+ href:
+ description: A link to the Web API endpoint providing full details of the track.
+ type: string
+ id:
+ description: The Spotify ID for the track.
+ type: string
+ is_playable:
+ description: 'Part of the response when Track Relinking is applied. If true, the track is playable in the given market. Otherwise false.'
+ type: boolean
+ linked_from:
+ description: 'Part of the response when Track Relinking is applied, and the requested track has been replaced with different track. The track in the linked_from object contains information about the originally requested track.'
+ properties:
+ external_urls:
+ additionalProperties:
+ description: 'The type of the URL, for example: ''spotify'' - The Spotify URL for the object.'
+ type: string
+ description: Known external URLs for this track.
+ type: object
+ href:
+ description: A link to the Web API endpoint providing full details of the track.
+ type: string
+ id:
+ description: The Spotify ID for the track.
+ type: string
+ type:
+ description: 'The object type: ''track''.'
+ type: string
+ uri:
+ description: The Spotify URI for the track.
+ type: string
+ type: object
+ name:
+ description: The name of the track.
+ type: string
+ preview_url:
+ description: A URL to a 30 second preview (MP3 format) of the track.
+ type: string
+ track_number:
+ description: 'The number of the track. If an album has several discs, the track number is the number on the specified disc.'
+ type: integer
+ type:
+ description: 'The object type: ''track''.'
+ type: string
+ uri:
+ description: The Spotify URI for the track.
+ type: string
+ type: object
+ track-simple-page:
+ properties:
+ href:
+ description: A link to the Web API endpoint returning the full result of the request.
+ type: string
+ items:
+ description: The requested data.
+ items:
+ $ref: '#/definitions/track-simple'
+ type: array
+ limit:
+ description: The maximum number of items in the response (as set in the query or by default).
+ type: integer
+ next:
+ description: URL to the next page of items. (null if none)
+ type: string
+ offset:
+ description: The offset of the items returned (as set in the query or by default).
+ type: integer
+ previous:
+ description: URL to the previous page of items. (null if none)
+ type: string
+ total:
+ description: The total number of items available to return.
+ type: integer
+ type: object
+ user-followed:
+ properties:
+ artists:
+ description: Present if the type of followe items is 'artist'.
+ properties:
+ cursor:
+ description: The cursors used to find the next set of items.
+ properties:
+ after:
+ description: The cursor to use as key to find the next page of items.
+ type: string
+ type: object
+ href:
+ description: A link to the Web API endpoint returning the full result of the request.
+ type: string
+ items:
+ description: The requested data.
+ items:
+ $ref: '#/definitions/artist'
+ type: array
+ limit:
+ description: The maximum number of items in the response (as set in the query or by default).
+ type: integer
+ next:
+ description: URL to the next page of items. (null if none)
+ type: string
+ total:
+ description: The total number of items available to return.
+ type: integer
+ type: object
+ type: object
+ user-profile:
+ properties:
+ displayName:
+ description: The name displayed on the user's profile.
+ type: string
+ external_urls:
+ additionalProperties:
+ type: string
+ description: Known external URLs for this user.
+ type: object
+ followers:
+ $ref: '#/definitions/followers'
+ href:
+ description: A link to the Web API endpoint for this user.
+ type: string
+ id:
+ description: The Spotify ID for this user.
+ type: string
+ type:
+ description: 'The object type: ''user'''
+ type: string
+ uri:
+ description: The Spotify URI for the user.
+ type: string
+ type: object
diff --git a/test/test_cli/test_script.py b/test/test_cli/test_script.py
index aa866d5..aee4844 100644
--- a/test/test_cli/test_script.py
+++ b/test/test_cli/test_script.py
@@ -64,8 +64,10 @@ def test_help():
-l User - "{test_data_path / 'users.json'}" """,
id="list1_list2"),
- pytest.param(f"""{executable} -m Gist "{tmp_path / '*.gist'}" --dkf files""", id="gists"),
- pytest.param(f"""{executable} -m Gist "{tmp_path / '*.gist'}" --dkf files --datetime""", id="gists_datetime"),
+ pytest.param(f"""{executable} -m Gist "{tmp_path / '*.gist'}" --dkf files""",
+ id="gists"),
+ pytest.param(f"""{executable} -m Gist "{tmp_path / '*.gist'}" --dkf files --datetime""",
+ id="gists_datetime"),
pytest.param(f"""{executable} -m Gist "{tmp_path / '*.gist'}" --dkf files --merge percent number_10""",
id="gists_merge_policy"),
pytest.param(f"""{executable} -m Gist "{tmp_path / '*.gist'}" --dkf files --merge exact""",
@@ -81,6 +83,10 @@ def test_help():
id="dont_convert_unicode"),
pytest.param(f"""{executable} -m SomeUnicode "{test_data_path / 'unicode.json'}" --disable-unicode-conversion""",
id="dont_convert_unicode_2"),
+ pytest.param(f"""{executable} -m YamlFile "{test_data_path / 'spotify-swagger.yaml'}" -i yaml""",
+ id="yaml_file"),
+ pytest.param(f"""{executable} -m IniFile "{test_data_path / 'file.ini'}" -i ini""",
+ id="ini_file"),
]
diff --git a/test/test_cli/test_utils.py b/test/test_cli/test_utils.py
index 447ef3b..f8318de 100644
--- a/test/test_cli/test_utils.py
+++ b/test/test_cli/test_utils.py
@@ -1,9 +1,10 @@
+import json
import sys
from pathlib import Path
import pytest
-from json_to_models.cli import _process_path, dict_lookup, iter_json_file, path_split, safe_json_load
+from json_to_models.cli import _process_path, dict_lookup, iter_json_file, path_split
from json_to_models.utils import convert_args
echo = lambda *args, **kwargs: (args, kwargs)
@@ -97,8 +98,20 @@ def test_dict_lookup(value, expected):
test_iter_json_file_data = [
- pytest.param((path / "data" / "users.json", "-"), lambda data: len(data) == 10),
- pytest.param((path / "data" / "photos.json", "items"), lambda data: len(data) == 5000),
+ pytest.param(
+ (
+ json.load((path / "data" / "users.json").open()),
+ "-"
+ ),
+ lambda data: len(data) == 10
+ ),
+ pytest.param(
+ (
+ json.load((path / "data" / "photos.json").open()),
+ "items"
+ ),
+ lambda data: len(data) == 5000
+ ),
]
@@ -108,10 +121,6 @@ def test_iter_json_file(value, expected):
assert expected(result) is True, f"(in value: {value})"
-def test_safe_json_load():
- assert safe_json_load(path / "data" / "users.json")
-
-
abs_path = path.absolute()
abs_path_str = str(abs_path).replace("\\", "/")
# noinspection PyRedeclaration
From ada5f67039a75ae806caa565ae6a124d915a9bf2 Mon Sep 17 00:00:00 2001
From: bogdan_dm
Date: Tue, 13 Jul 2021 18:54:24 +0300
Subject: [PATCH 2/4] Fix invalid chars in Literal value
---
json_to_models/dynamic_typing/complex.py | 5 ++++-
test/test_cli/data/file.ini | 2 +-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/json_to_models/dynamic_typing/complex.py b/json_to_models/dynamic_typing/complex.py
index 6fba7a5..5d42439 100644
--- a/json_to_models/dynamic_typing/complex.py
+++ b/json_to_models/dynamic_typing/complex.py
@@ -265,7 +265,10 @@ def to_typing_code(self, types_style: Dict[Union['BaseType', Type['BaseType']],
if options.get(self.TypeStyle.use_literals):
limit = options.get(self.TypeStyle.max_literals)
if limit is None or len(self.literals) < limit:
- parts = ', '.join(f'"{s}"' for s in sorted(self.literals))
+ parts = ', '.join(
+ '"{}"'.format(s.replace('\\', '\\\\').replace('"', '\\"'))
+ for s in sorted(self.literals)
+ )
return [(Literal.__module__, 'Literal')], f"Literal[{parts}]"
return [], 'str'
diff --git a/test/test_cli/data/file.ini b/test/test_cli/data/file.ini
index fb52254..f87481c 100644
--- a/test/test_cli/data/file.ini
+++ b/test/test_cli/data/file.ini
@@ -6,4 +6,4 @@ organization = Acme Widgets Inc.
; use IP address in case network name resolution is not working
server = 192.0.2.62
port = 143
-file = payroll.dat
+file = "payroll.dat"
From 982bd2edefc11cf4cacdc71b2c990482f9b5f898 Mon Sep 17 00:00:00 2001
From: bogdan_dm
Date: Tue, 13 Jul 2021 19:59:28 +0300
Subject: [PATCH 3/4] Add docs; Change default yaml parser; Handle non-str keys
in dicts
---
README.md | 162 +++++++++++++++++++++---
json_to_models/cli.py | 6 +-
json_to_models/generator.py | 5 +
setup.py | 2 +-
test/test_generator/test_detect_type.py | 2 +-
5 files changed, 157 insertions(+), 20 deletions(-)
diff --git a/README.md b/README.md
index 133a90c..9dd8154 100644
--- a/README.md
+++ b/README.md
@@ -277,15 +277,138 @@ class Response:
@dataclass
class Definition_Schema:
- type_: str
- required: Optional[List[str]] = field(default_factory=list)
- properties: Optional[Dict[str, Union['Property', 'Property_2E']]] = field(default_factory=dict)
- ref: Optional[str] = None
+ type_: str
+ required: Optional[List[str]] = field(default_factory=list)
+ properties: Optional[Dict[str, Union['Property', 'Property_2E']]] = field(default_factory=dict)
+ ref: Optional[str] = None
```
+### Github-actions config files
+
+----- Show -----
+
+
+Github-actions model based on files from [starter-workflows](https://github.com/actions/starter-workflows/tree/main/ci)
+
+```
+json2models -m Actions "./starter-workflows/ci/*.yml" -s flat -f pydantic -i yaml --dkf env with jobs
+```
+
+```python
+r"""
+generated by json2python-models v0.2.3 at Tue Jul 13 19:52:43 2021
+command: /opt/projects/json2python-models/venv/bin/json2models -m Actions ./starter-workflows/ci/*.yml -s flat -f pydantic -i yaml --dkf env with jobs
+"""
+from pydantic import BaseModel, Field
+from typing import Dict, List, Optional, Union
+from typing_extensions import Literal
+
+
+class Actions(BaseModel):
+ on: Union['On', List[Literal["push"]]]
+ jobs: Dict[str, 'Job']
+ name: Optional[str] = None
+ env: Optional[Dict[str, Union[int, str]]] = {}
+
+
+class On(BaseModel):
+ push: Optional['Push'] = None
+ pull_request: Optional['PullRequest'] = None
+ release: Optional['Release'] = None
+ schedule: Optional[List['Schedule']] = []
+ workflow_dispatch: Optional[None] = None
+
+
+class Push(BaseModel):
+ branches: List[Literal["$default-branch"]]
+ tags: Optional[List[Literal["v*.*.*"]]] = []
+
+
+class PullRequest(BaseModel):
+ branches: List[Literal["$default-branch"]]
+
+
+class Release(BaseModel):
+ types: List[Literal["created", "published"]]
+
+
+class Schedule(BaseModel):
+ cron: Literal["$cron-daily"]
+
+
+class Job(BaseModel):
+ runson: Literal[
+ "${{ matrix.os }}", "macOS-latest", "macos-latest", "ubuntu-18.04", "ubuntu-latest", "windows-latest"] = Field(
+ ..., alias="runs-on")
+ steps: List['Step']
+ name: Optional[str] = None
+ environment: Optional[Literal["production"]] = None
+ outputs: Optional['Output'] = None
+ container: Optional['Container'] = None
+ needs: Optional[Literal["build"]] = None
+ permissions: Optional['Permission'] = None
+ strategy: Optional['Strategy'] = None
+ defaults: Optional['Default'] = None
+ env: Optional[Dict[str, str]] = {}
+
+
+class Step(BaseModel):
+ uses: Optional[str] = None
+ name: Optional[str] = None
+ with_: Optional[Dict[str, Union[bool, float, str]]] = Field({}, alias="with")
+ run: Optional[str] = None
+ env: Optional[Dict[str, str]] = {}
+ workingdirectory: Optional[str] = Field(None, alias="working-directory")
+ id_: Optional[Literal[
+ "build-image", "composer-cache", "deploy-and-expose", "image-build", "login-ecr", "meta", "push-to-registry", "task-def"]] = Field(
+ None, alias="id")
+ if_: Optional[str] = Field(None, alias="if")
+ shell: Optional[Literal["Rscript {0}"]] = None
+
+
+class Output(BaseModel):
+ route: str = Field(..., alias="ROUTE")
+ selector: str = Field(..., alias="SELECTOR")
+
+
+class Container(BaseModel):
+ image: Literal["crystallang/crystal", "erlang:22.0.7"]
+
+
+class Permission(BaseModel):
+ contents: Literal["read"]
+ packages: Literal["write"]
+
+
+class Strategy(BaseModel):
+ matrix: Optional['Matrix'] = None
+ maxparallel: Optional[int] = Field(None, alias="max-parallel")
+ failfast: Optional[bool] = Field(None, alias="fail-fast")
+
+
+class Matrix(BaseModel):
+ rversion: Optional[List[float]] = Field([], alias="r-version")
+ pythonversion: Optional[List[float]] = Field([], alias="python-version")
+ deno: Optional[List[Literal["canary", "v1.x"]]] = []
+ os: Optional[List[Literal["macOS-latest", "ubuntu-latest", "windows-latest"]]] = []
+ rubyversion: Optional[List[float]] = Field([], alias="ruby-version")
+ nodeversion: Optional[List[Literal["12.x", "14.x", "16.x"]]] = Field([], alias="node-version")
+ configuration: Optional[List[Literal["Debug", "Release"]]] = []
+
+
+class Default(BaseModel):
+ run: 'Run'
+
+
+class Run(BaseModel):
+ shell: Literal["bash"]
+```
+
+
+
## Installation
| **Be ware**: this project supports only `python3.7` and higher. |
@@ -315,24 +438,33 @@ json2models -m Car car_*.json -f attrs > car.py
Arguments:
* `-h`, `--help` - Show help message and exit
-
-* `-m`, `--model` - Model name and its JSON data as path or unix-like path pattern. `*`, `**` or `?` patterns symbols are supported.
+
+* `-m`, `--model` - Model name and its JSON data as path or unix-like path pattern. `*`, `**` or `?` patterns symbols
+ are supported.
* **Format**: `-m [ ...]`
* **Example**: `-m Car audi.json reno.json` or `-m Car audi.json -m Car reno.json` (results will be the same)
-
-* `-l`, `--list` - Like `-m` but given json file should contain list of model data (dataset).
- If this file contains dict with nested list than you can pass `` to lookup.
- Deep lookups are supported by dot-separated path. If no lookup needed pass `-` as ``.
+
+* `-l`, `--list` - Like `-m` but given json file should contain list of model data (dataset). If this file contains dict
+ with nested list than you can pass `` to lookup. Deep lookups are supported by dot-separated path. If no
+ lookup needed pass `-` as ``.
* **Format**: `-l `
* **Example**: `-l Car - cars.json -l Person fetch_results.items.persons result.json`
- * **Note**: Models names under this arguments should be unique.
-
+ * **Note**: Models names under these arguments should be unique.
+
+* `-i`, `--input-format` - Input file format (parser). Default is JSON parser. Yaml parser requires PyYaml or
+ ruamel.yaml to be installed. Ini parser uses
+ builtin [configparser](https://docs.python.org/3/library/configparser.html). To implement new one - add new method
+ to `cli.FileLoaders` (and create pull request :) )
+ * **Format**: `-i {json, yaml, ini}`
+ * **Example**: `-i yaml`
+ * **Default**: `-i json`
+
* `-o`, `--output` - Output file
* **Format**: `-o `
* **Example**: `-o car_model.py`
-
-* `-f`, `--framework` - Model framework for which python code is generated.
- `base` (default) mean no framework so code will be generated without any decorators and additional meta-data.
+
+* `-f`, `--framework` - Model framework for which python code is generated.
+ `base` (default) mean no framework so code will be generated without any decorators and additional meta-data.
* **Format**: `-f {base, pydantic, attrs, dataclasses, custom}`
* **Example**: `-f pydantic`
* **Default**: `-f base`
diff --git a/json_to_models/cli.py b/json_to_models/cli.py
index 573ea98..20222c1 100644
--- a/json_to_models/cli.py
+++ b/json_to_models/cli.py
@@ -12,10 +12,10 @@
from typing import Any, Callable, Dict, Generator, Iterable, List, Tuple, Type, Union
try:
- import yaml
+ import ruamel.yaml as yaml
except ImportError:
try:
- import ruamel.yaml as yaml
+ import yaml
except ImportError:
yaml = None
@@ -268,7 +268,7 @@ def _create_argparser(cls) -> argparse.ArgumentParser:
)
parser.add_argument(
"-i", "--input-format",
- metavar="FORMAT", default="json",
+ default="json",
choices=['json', 'yaml', 'ini'],
help="Input files parser ('PyYaml' is required to parse yaml files)\n\n"
)
diff --git a/json_to_models/generator.py b/json_to_models/generator.py
index 603fc5c..5184737 100644
--- a/json_to_models/generator.py
+++ b/json_to_models/generator.py
@@ -56,6 +56,11 @@ def _convert(self, data: dict):
"""
fields = dict()
for key, value in data.items():
+ if not isinstance(key, str):
+ raise TypeError(f'You probably using some not JSON-compatible parser and have some {type(key)} as dict key. '
+ f'This is not supported.\n'
+ f'Context: {data}\n'
+ f'(If you parsing yaml try to replace PyYaml with ruamel.yaml)')
convert_dict = key not in self.dict_keys_fields
fields[key] = self._detect_type(value, convert_dict)
return fields
diff --git a/setup.py b/setup.py
index af89a0d..6362a1f 100644
--- a/setup.py
+++ b/setup.py
@@ -50,6 +50,6 @@ def run_tests(self):
},
install_requires=required,
cmdclass={"test": PyTest},
- tests_require=["pytest>=4.4.0", "pytest-xdist", "requests", "attrs", "pydantic>=1.3", "PyYaml"],
+ tests_require=["pytest>=4.4.0", "pytest-xdist", "requests", "attrs", "pydantic>=1.3", "ruamel.yaml"],
data_files=[('', ['requirements.txt', 'pytest.ini', '.coveragerc', 'LICENSE', 'README.md', 'CHANGELOG.md'])]
)
diff --git a/test/test_generator/test_detect_type.py b/test/test_generator/test_detect_type.py
index b2398fe..2f11c86 100644
--- a/test/test_generator/test_detect_type.py
+++ b/test/test_generator/test_detect_type.py
@@ -28,7 +28,7 @@
pytest.param("1.0", FloatString, id="float_str"),
pytest.param("true", BooleanString, id="bool_str"),
pytest.param({"test_dict_field_a": 1, "test_dict_field_b": "a"}, DDict(DUnion(int, StringLiteral({"a"}))), id="simple_dict"),
- pytest.param({}, DDict(Unknown))
+ pytest.param({}, DDict(Unknown), id="empty_dict")
]
test_dict = {param.id: param.values[0] for param in test_data}
From 9895ea726e9191e50ba923b0466b391e93dfd1a2 Mon Sep 17 00:00:00 2001
From: bogdan_dm
Date: Tue, 13 Jul 2021 20:01:40 +0300
Subject: [PATCH 4/4] Replace tabs in doc
---
README.md | 114 ++++++++++++++++++++++++++----------------------------
1 file changed, 55 insertions(+), 59 deletions(-)
diff --git a/README.md b/README.md
index 9dd8154..2838458 100644
--- a/README.md
+++ b/README.md
@@ -277,10 +277,10 @@ class Response:
@dataclass
class Definition_Schema:
- type_: str
- required: Optional[List[str]] = field(default_factory=list)
- properties: Optional[Dict[str, Union['Property', 'Property_2E']]] = field(default_factory=dict)
- ref: Optional[str] = None
+ type_: str
+ required: Optional[List[str]] = field(default_factory=list)
+ properties: Optional[Dict[str, Union['Property', 'Property_2E']]] = field(default_factory=dict)
+ ref: Optional[str] = None
```
@@ -308,103 +308,99 @@ from typing_extensions import Literal
class Actions(BaseModel):
- on: Union['On', List[Literal["push"]]]
- jobs: Dict[str, 'Job']
- name: Optional[str] = None
- env: Optional[Dict[str, Union[int, str]]] = {}
+ on: Union['On', List[Literal["push"]]]
+ jobs: Dict[str, 'Job']
+ name: Optional[str] = None
+ env: Optional[Dict[str, Union[int, str]]] = {}
class On(BaseModel):
- push: Optional['Push'] = None
- pull_request: Optional['PullRequest'] = None
- release: Optional['Release'] = None
- schedule: Optional[List['Schedule']] = []
- workflow_dispatch: Optional[None] = None
+ push: Optional['Push'] = None
+ pull_request: Optional['PullRequest'] = None
+ release: Optional['Release'] = None
+ schedule: Optional[List['Schedule']] = []
+ workflow_dispatch: Optional[None] = None
class Push(BaseModel):
- branches: List[Literal["$default-branch"]]
- tags: Optional[List[Literal["v*.*.*"]]] = []
+ branches: List[Literal["$default-branch"]]
+ tags: Optional[List[Literal["v*.*.*"]]] = []
class PullRequest(BaseModel):
- branches: List[Literal["$default-branch"]]
+ branches: List[Literal["$default-branch"]]
class Release(BaseModel):
- types: List[Literal["created", "published"]]
+ types: List[Literal["created", "published"]]
class Schedule(BaseModel):
- cron: Literal["$cron-daily"]
+ cron: Literal["$cron-daily"]
class Job(BaseModel):
- runson: Literal[
- "${{ matrix.os }}", "macOS-latest", "macos-latest", "ubuntu-18.04", "ubuntu-latest", "windows-latest"] = Field(
- ..., alias="runs-on")
- steps: List['Step']
- name: Optional[str] = None
- environment: Optional[Literal["production"]] = None
- outputs: Optional['Output'] = None
- container: Optional['Container'] = None
- needs: Optional[Literal["build"]] = None
- permissions: Optional['Permission'] = None
- strategy: Optional['Strategy'] = None
- defaults: Optional['Default'] = None
- env: Optional[Dict[str, str]] = {}
+ runson: Literal["${{ matrix.os }}", "macOS-latest", "macos-latest", "ubuntu-18.04", "ubuntu-latest", "windows-latest"] = Field(..., alias="runs-on")
+ steps: List['Step']
+ name: Optional[str] = None
+ environment: Optional[Literal["production"]] = None
+ outputs: Optional['Output'] = None
+ container: Optional['Container'] = None
+ needs: Optional[Literal["build"]] = None
+ permissions: Optional['Permission'] = None
+ strategy: Optional['Strategy'] = None
+ defaults: Optional['Default'] = None
+ env: Optional[Dict[str, str]] = {}
class Step(BaseModel):
- uses: Optional[str] = None
- name: Optional[str] = None
- with_: Optional[Dict[str, Union[bool, float, str]]] = Field({}, alias="with")
- run: Optional[str] = None
- env: Optional[Dict[str, str]] = {}
- workingdirectory: Optional[str] = Field(None, alias="working-directory")
- id_: Optional[Literal[
- "build-image", "composer-cache", "deploy-and-expose", "image-build", "login-ecr", "meta", "push-to-registry", "task-def"]] = Field(
- None, alias="id")
- if_: Optional[str] = Field(None, alias="if")
- shell: Optional[Literal["Rscript {0}"]] = None
+ uses: Optional[str] = None
+ name: Optional[str] = None
+ with_: Optional[Dict[str, Union[bool, float, str]]] = Field({}, alias="with")
+ run: Optional[str] = None
+ env: Optional[Dict[str, str]] = {}
+ workingdirectory: Optional[str] = Field(None, alias="working-directory")
+ id_: Optional[Literal["build-image", "composer-cache", "deploy-and-expose", "image-build", "login-ecr", "meta", "push-to-registry", "task-def"]] = Field(None, alias="id")
+ if_: Optional[str] = Field(None, alias="if")
+ shell: Optional[Literal["Rscript {0}"]] = None
class Output(BaseModel):
- route: str = Field(..., alias="ROUTE")
- selector: str = Field(..., alias="SELECTOR")
+ route: str = Field(..., alias="ROUTE")
+ selector: str = Field(..., alias="SELECTOR")
class Container(BaseModel):
- image: Literal["crystallang/crystal", "erlang:22.0.7"]
+ image: Literal["crystallang/crystal", "erlang:22.0.7"]
class Permission(BaseModel):
- contents: Literal["read"]
- packages: Literal["write"]
+ contents: Literal["read"]
+ packages: Literal["write"]
class Strategy(BaseModel):
- matrix: Optional['Matrix'] = None
- maxparallel: Optional[int] = Field(None, alias="max-parallel")
- failfast: Optional[bool] = Field(None, alias="fail-fast")
+ matrix: Optional['Matrix'] = None
+ maxparallel: Optional[int] = Field(None, alias="max-parallel")
+ failfast: Optional[bool] = Field(None, alias="fail-fast")
class Matrix(BaseModel):
- rversion: Optional[List[float]] = Field([], alias="r-version")
- pythonversion: Optional[List[float]] = Field([], alias="python-version")
- deno: Optional[List[Literal["canary", "v1.x"]]] = []
- os: Optional[List[Literal["macOS-latest", "ubuntu-latest", "windows-latest"]]] = []
- rubyversion: Optional[List[float]] = Field([], alias="ruby-version")
- nodeversion: Optional[List[Literal["12.x", "14.x", "16.x"]]] = Field([], alias="node-version")
- configuration: Optional[List[Literal["Debug", "Release"]]] = []
+ rversion: Optional[List[float]] = Field([], alias="r-version")
+ pythonversion: Optional[List[float]] = Field([], alias="python-version")
+ deno: Optional[List[Literal["canary", "v1.x"]]] = []
+ os: Optional[List[Literal["macOS-latest", "ubuntu-latest", "windows-latest"]]] = []
+ rubyversion: Optional[List[float]] = Field([], alias="ruby-version")
+ nodeversion: Optional[List[Literal["12.x", "14.x", "16.x"]]] = Field([], alias="node-version")
+ configuration: Optional[List[Literal["Debug", "Release"]]] = []
class Default(BaseModel):
- run: 'Run'
+ run: 'Run'
class Run(BaseModel):
- shell: Literal["bash"]
+ shell: Literal["bash"]
```