Skip to content

Commit f2a495e

Browse files
committed
Added ability to generate clients with no metadata or setup.py. Closes #120
1 parent bbc550e commit f2a495e

File tree

13 files changed

+388
-131
lines changed

13 files changed

+388
-131
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## Unreleased
9+
### Additions
10+
- New `--meta` command line option for specifying what type of metadata should be generated:
11+
- `poetry` is the default value, same behavior you're used to in previous versions
12+
- `setup` will generate a pyproject.toml with no Poetry information, and instead create a `setup.py` with the
13+
project info.
14+
- `none` will not create a project folder at all, only the inner package folder (which won't be inner anymore)
15+
816
## 0.7.2 - 2020-12-08
917
### Fixes
1018
- A bug in handling optional properties that are themselves models (introduced in 0.7.1) (#262). Thanks @packyg!

end_to_end_tests/golden-record-custom/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ include = ["CHANGELOG.md", "custom_e2e/py.typed"]
1414

1515
[tool.poetry.dependencies]
1616
python = "^3.6"
17-
httpx = "^0.15.0"
17+
httpx = ">=0.15.4,<0.17.0"
1818
attrs = "^20.1.0"
1919
python-dateutil = "^2.8.1"
2020

end_to_end_tests/golden-record/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ include = ["CHANGELOG.md", "my_test_api_client/py.typed"]
1414

1515
[tool.poetry.dependencies]
1616
python = "^3.6"
17-
httpx = "^0.15.0"
17+
httpx = ">=0.15.4,<0.17.0"
1818
attrs = "^20.1.0"
1919
python-dateutil = "^2.8.1"
2020

openapi_python_client/__init__.py

Lines changed: 61 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import shutil
44
import subprocess
55
import sys
6+
from enum import Enum
67
from pathlib import Path
78
from typing import Any, Dict, Optional, Sequence, Union
89

@@ -25,14 +26,21 @@
2526
__version__ = version(__package__)
2627

2728

29+
class MetaType(str, Enum):
30+
NONE = "none"
31+
POETRY = "poetry"
32+
SETUP = "setup"
33+
34+
2835
class Project:
2936
TEMPLATE_FILTERS = {"snakecase": utils.snake_case, "kebabcase": utils.kebab_case, "pascalcase": utils.pascal_case}
3037
project_name_override: Optional[str] = None
3138
package_name_override: Optional[str] = None
3239
package_version_override: Optional[str] = None
3340

34-
def __init__(self, *, openapi: GeneratorData, custom_template_path: Optional[Path] = None) -> None:
41+
def __init__(self, *, openapi: GeneratorData, meta: MetaType, custom_template_path: Optional[Path] = None) -> None:
3542
self.openapi: GeneratorData = openapi
43+
self.meta: MetaType = meta
3644

3745
package_loader = PackageLoader(__package__)
3846
loader: BaseLoader
@@ -48,7 +56,9 @@ def __init__(self, *, openapi: GeneratorData, custom_template_path: Optional[Pat
4856
self.env: Environment = Environment(loader=loader, trim_blocks=True, lstrip_blocks=True)
4957

5058
self.project_name: str = self.project_name_override or f"{utils.kebab_case(openapi.title).lower()}-client"
51-
self.project_dir: Path = Path.cwd() / self.project_name
59+
self.project_dir: Path = Path.cwd()
60+
if meta != MetaType.NONE:
61+
self.project_dir /= self.project_name
5262

5363
self.package_name: str = self.package_name_override or self.project_name.replace("-", "_")
5464
self.package_dir: Path = self.project_dir / self.package_name
@@ -62,11 +72,14 @@ def __init__(self, *, openapi: GeneratorData, custom_template_path: Optional[Pat
6272
def build(self) -> Sequence[GeneratorError]:
6373
""" Create the project from templates """
6474

65-
print(f"Generating {self.project_name}")
66-
try:
67-
self.project_dir.mkdir()
68-
except FileExistsError:
69-
return [GeneratorError(detail="Directory already exists. Delete it or use the update command.")]
75+
if self.meta == MetaType.NONE:
76+
print(f"Generating {self.package_name}")
77+
else:
78+
print(f"Generating {self.project_name}")
79+
try:
80+
self.project_dir.mkdir()
81+
except FileExistsError:
82+
return [GeneratorError(detail="Directory already exists. Delete it or use the update command.")]
7083
self._create_package()
7184
self._build_metadata()
7285
self._build_models()
@@ -79,7 +92,7 @@ def update(self) -> Sequence[GeneratorError]:
7992

8093
if not self.package_dir.is_dir():
8194
raise FileNotFoundError()
82-
print(f"Updating {self.project_name}")
95+
print(f"Updating {self.package_name}")
8396
shutil.rmtree(self.package_dir)
8497
self._create_package()
8598
self._build_models()
@@ -119,25 +132,21 @@ def _create_package(self) -> None:
119132
package_init_template = self.env.get_template("package_init.pyi")
120133
package_init.write_text(package_init_template.render(description=self.package_description))
121134

122-
pytyped = self.package_dir / "py.typed"
123-
pytyped.write_text("# Marker file for PEP 561")
135+
if self.meta != MetaType.NONE:
136+
pytyped = self.package_dir / "py.typed"
137+
pytyped.write_text("# Marker file for PEP 561")
124138

125139
types_template = self.env.get_template("types.py")
126140
types_path = self.package_dir / "types.py"
127141
types_path.write_text(types_template.render())
128142

129143
def _build_metadata(self) -> None:
130-
# Create a pyproject.toml file
131-
pyproject_template = self.env.get_template("pyproject.toml")
132-
pyproject_path = self.project_dir / "pyproject.toml"
133-
pyproject_path.write_text(
134-
pyproject_template.render(
135-
project_name=self.project_name,
136-
package_name=self.package_name,
137-
version=self.version,
138-
description=self.package_description,
139-
)
140-
)
144+
if self.meta == MetaType.NONE:
145+
return
146+
147+
self._build_pyproject_toml(use_poetry=self.meta == MetaType.POETRY)
148+
if self.meta == MetaType.SETUP:
149+
self._build_setup_py()
141150

142151
# README.md
143152
readme = self.project_dir / "README.md"
@@ -153,6 +162,31 @@ def _build_metadata(self) -> None:
153162
git_ignore_template = self.env.get_template(".gitignore")
154163
git_ignore_path.write_text(git_ignore_template.render())
155164

165+
def _build_pyproject_toml(self, *, use_poetry: bool) -> None:
166+
template = "pyproject.toml" if use_poetry else "pyproject_no_poetry.toml"
167+
pyproject_template = self.env.get_template(template)
168+
pyproject_path = self.project_dir / "pyproject.toml"
169+
pyproject_path.write_text(
170+
pyproject_template.render(
171+
project_name=self.project_name,
172+
package_name=self.package_name,
173+
version=self.version,
174+
description=self.package_description,
175+
)
176+
)
177+
178+
def _build_setup_py(self) -> None:
179+
template = self.env.get_template("setup.py")
180+
path = self.project_dir / "setup.py"
181+
path.write_text(
182+
template.render(
183+
project_name=self.project_name,
184+
package_name=self.package_name,
185+
version=self.version,
186+
description=self.package_description,
187+
)
188+
)
189+
156190
def _build_models(self) -> None:
157191
# Generate models
158192
models_dir = self.package_dir / "models"
@@ -205,42 +239,42 @@ def _build_api(self) -> None:
205239

206240

207241
def _get_project_for_url_or_path(
208-
url: Optional[str], path: Optional[Path], custom_template_path: Optional[Path] = None
242+
url: Optional[str], path: Optional[Path], meta: MetaType, custom_template_path: Optional[Path] = None
209243
) -> Union[Project, GeneratorError]:
210244
data_dict = _get_document(url=url, path=path)
211245
if isinstance(data_dict, GeneratorError):
212246
return data_dict
213247
openapi = GeneratorData.from_dict(data_dict)
214248
if isinstance(openapi, GeneratorError):
215249
return openapi
216-
return Project(openapi=openapi, custom_template_path=custom_template_path)
250+
return Project(openapi=openapi, custom_template_path=custom_template_path, meta=meta)
217251

218252

219253
def create_new_client(
220-
*, url: Optional[str], path: Optional[Path], custom_template_path: Optional[Path] = None
254+
*, url: Optional[str], path: Optional[Path], meta: MetaType, custom_template_path: Optional[Path] = None
221255
) -> Sequence[GeneratorError]:
222256
"""
223257
Generate the client library
224258
225259
Returns:
226260
A list containing any errors encountered when generating.
227261
"""
228-
project = _get_project_for_url_or_path(url=url, path=path, custom_template_path=custom_template_path)
262+
project = _get_project_for_url_or_path(url=url, path=path, custom_template_path=custom_template_path, meta=meta)
229263
if isinstance(project, GeneratorError):
230264
return [project]
231265
return project.build()
232266

233267

234268
def update_existing_client(
235-
*, url: Optional[str], path: Optional[Path], custom_template_path: Optional[Path] = None
269+
*, url: Optional[str], path: Optional[Path], meta: MetaType, custom_template_path: Optional[Path] = None
236270
) -> Sequence[GeneratorError]:
237271
"""
238272
Update an existing client library
239273
240274
Returns:
241275
A list containing any errors encountered when generating.
242276
"""
243-
project = _get_project_for_url_or_path(url=url, path=path, custom_template_path=custom_template_path)
277+
project = _get_project_for_url_or_path(url=url, path=path, custom_template_path=custom_template_path, meta=meta)
244278
if isinstance(project, GeneratorError):
245279
return [project]
246280
return project.update()

openapi_python_client/cli.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import typer
66

7+
from openapi_python_client import MetaType
78
from openapi_python_client.parser.errors import ErrorLevel, GeneratorError, ParseError
89

910
app = typer.Typer()
@@ -104,12 +105,18 @@ def handle_errors(errors: Sequence[GeneratorError]) -> None:
104105
"resolve_path": True,
105106
}
106107

108+
_meta_option = typer.Option(
109+
MetaType.POETRY,
110+
help="The type of metadata you want to generate.",
111+
)
112+
107113

108114
@app.command()
109115
def generate(
110116
url: Optional[str] = typer.Option(None, help="A URL to read the JSON from"),
111117
path: Optional[pathlib.Path] = typer.Option(None, help="A path to the JSON file"),
112118
custom_template_path: Optional[pathlib.Path] = typer.Option(None, **custom_template_path_options), # type: ignore
119+
meta: MetaType = _meta_option,
113120
) -> None:
114121
""" Generate a new OpenAPI Client library """
115122
from . import create_new_client
@@ -120,7 +127,7 @@ def generate(
120127
if url and path:
121128
typer.secho("Provide either --url or --path, not both", fg=typer.colors.RED)
122129
raise typer.Exit(code=1)
123-
errors = create_new_client(url=url, path=path, custom_template_path=custom_template_path)
130+
errors = create_new_client(url=url, path=path, meta=meta, custom_template_path=custom_template_path)
124131
handle_errors(errors)
125132

126133

@@ -129,6 +136,7 @@ def update(
129136
url: Optional[str] = typer.Option(None, help="A URL to read the JSON from"),
130137
path: Optional[pathlib.Path] = typer.Option(None, help="A path to the JSON file"),
131138
custom_template_path: Optional[pathlib.Path] = typer.Option(None, **custom_template_path_options), # type: ignore
139+
meta: MetaType = _meta_option,
132140
) -> None:
133141
""" Update an existing OpenAPI Client library """
134142
from . import update_existing_client
@@ -140,5 +148,5 @@ def update(
140148
typer.secho("Provide either --url or --path, not both", fg=typer.colors.RED)
141149
raise typer.Exit(code=1)
142150

143-
errors = update_existing_client(url=url, path=path, custom_template_path=custom_template_path)
151+
errors = update_existing_client(url=url, path=path, meta=meta, custom_template_path=custom_template_path)
144152
handle_errors(errors)

openapi_python_client/templates/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ include = ["CHANGELOG.md", "{{ package_name }}/py.typed"]
1414

1515
[tool.poetry.dependencies]
1616
python = "^3.6"
17-
httpx = "^0.15.0"
17+
httpx = ">=0.15.4,<0.17.0"
1818
attrs = "^20.1.0"
1919
python-dateutil = "^2.8.1"
2020

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[tool.black]
2+
line-length = 120
3+
target_version = ['py36', 'py37', 'py38']
4+
exclude = '''
5+
(
6+
/(
7+
| \.git
8+
| \.venv
9+
| \.mypy_cache
10+
)/
11+
)
12+
'''
13+
14+
[tool.isort]
15+
line_length = 120
16+
multi_line_output = 3
17+
include_trailing_comma = true
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import pathlib
2+
3+
from setuptools import find_packages, setup
4+
5+
here = pathlib.Path(__file__).parent.resolve()
6+
long_description = (here / "README.md").read_text(encoding="utf-8")
7+
8+
setup(
9+
name="{{ project_name }}",
10+
version="{{ version }}",
11+
description="{{ description }}",
12+
long_description=long_description,
13+
long_description_content_type="text/markdown",
14+
package_dir={"": "{{ package_name }}"},
15+
packages=find_packages(where="{{ package_name }}"),
16+
python_requires=">=3.6, <4",
17+
install_requires=["httpx >= 0.15.0, < 0.17.0", "attrs >= 20.1.0", "python-dateutil >= 2.8.1, < 3"],
18+
package_data={"": ["CHANGELOG.md"], "{{ package_name }}": ["py.typed"]},
19+
)

0 commit comments

Comments
 (0)