Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions polyapi/poly_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,20 +121,20 @@ def add_schema_file(
# Read current __init__.py content if it exists
init_content = ""
if os.path.exists(init_path):
with open(init_path, "r") as f:
with open(init_path, "r", encoding='utf-8') as f:
init_content = f.read()

# Prepare new content to append to __init__.py
new_init_content = init_content + f"\n\nfrom ._{schema_namespace} import {schema_name}\n__all__.append('{schema_name}')\n"

# Use temporary files for atomic writes
# Write to __init__.py atomically
with tempfile.NamedTemporaryFile(mode="w", delete=False, dir=full_path, suffix=".tmp") as temp_init:
with tempfile.NamedTemporaryFile(mode="w", delete=False, dir=full_path, suffix=".tmp", encoding='utf-8') as temp_init:
temp_init.write(new_init_content)
temp_init_path = temp_init.name

# Write to schema file atomically
with tempfile.NamedTemporaryFile(mode="w", delete=False, dir=full_path, suffix=".tmp") as temp_schema:
with tempfile.NamedTemporaryFile(mode="w", delete=False, dir=full_path, suffix=".tmp", encoding='utf-8') as temp_schema:
temp_schema.write(schema_defs)
temp_schema_path = temp_schema.name

Expand Down Expand Up @@ -205,7 +205,7 @@ def create_schema(
def add_schema_to_init(full_path: str, spec: SchemaSpecDto):
init_the_init(full_path, code_imports="")
init_path = os.path.join(full_path, "__init__.py")
with open(init_path, "a") as f:
with open(init_path, "a", encoding='utf-8') as f:
f.write(render_poly_schema(spec) + "\n\n")


Expand Down
2 changes: 1 addition & 1 deletion polyapi/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def generate_schema_types(input_data: Dict, root=None):
with contextlib.redirect_stdout(None):
process_config(config, [tmp_input])

with open(tmp_output) as f:
with open(tmp_output, encoding='utf-8') as f:
output = f.read()

output = clean_malformed_examples(output)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ requires = ["setuptools>=61.2", "wheel"]

[project]
name = "polyapi-python"
version = "0.3.8"
version = "0.3.9.dev1"
description = "The Python Client for PolyAPI, the IPaaS by Developers for Developers"
authors = [{ name = "Dan Fellin", email = "dan@polyapi.io" }]
dependencies = [
Expand Down
18 changes: 16 additions & 2 deletions tests/test_schema.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import unittest
from polyapi.schema import clean_malformed_examples, wrapped_generate_schema_types
from polyapi.schema import clean_malformed_examples, wrapped_generate_schema_types, generate_schema_types

SCHEMA = {
"$schema": "http://json-schema.org/draft-06/schema#",
Expand All @@ -10,6 +10,14 @@
"definitions": {},
}

CHARACTER_SCHEMA = {
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"properties": {"CHARACTER_SCHEMA_NAME": {"description": "This is — “bad”, right?", "type": "string"}},
"additionalProperties": False,
"definitions": {},
}

APALEO_MALFORMED_EXAMPLE = 'from typing import List, TypedDict, Union\nfrom typing_extensions import Required\n\n\n# Body.\n# \n# example: {\n "from": "2024-04-21",\n "to": "2024-04-24",\n "grossDailyRate": {\n "amount": 160.0,\n "currency": "EUR"\n },\n "timeSlices": [\n {\n "blockedUnits": 3\n },\n {\n "blockedUnits": 0\n },\n {\n "blockedUnits": 7\n }\n ]\n}\n# x-readme-ref-name: ReplaceBlockModel\nBody = TypedDict(\'Body\', {\n # Start date and time from which the inventory will be blockedSpecify either a pure date or a date and time (without fractional second part) in UTC or with UTC offset as defined in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO8601:2004</a>\n # \n # Required property\n \'from\': Required[str],\n # End date and time until which the inventory will be blocked. Cannot be more than 5 years after the start date.Specify either a pure date or a date and time (without fractional second part) in UTC or with UTC offset as defined in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO8601:2004</a>\n # \n # Required property\n \'to\': Required[str],\n # x-readme-ref-name: MonetaryValueModel\n # \n # Required property\n \'grossDailyRate\': Required["_BodygrossDailyRate"],\n # The list of time slices\n # \n # Required property\n \'timeSlices\': Required[List["_BodytimeSlicesitem"]],\n}, total=False)\n\n\nclass _BodygrossDailyRate(TypedDict, total=False):\n """ x-readme-ref-name: MonetaryValueModel """\n\n amount: Required[Union[int, float]]\n """\n format: double\n\n Required property\n """\n\n currency: Required[str]\n """ Required property """\n\n\n\nclass _BodytimeSlicesitem(TypedDict, total=False):\n """ x-readme-ref-name: CreateBlockTimeSliceModel """\n\n blockedUnits: Required[Union[int, float]]\n """\n Number of units blocked for the time slice\n\n format: int32\n\n Required property\n """\n\n'


Expand All @@ -23,4 +31,10 @@ def test_fix_titles(self):

def test_clean_malformed_examples(self):
output = clean_malformed_examples(APALEO_MALFORMED_EXAMPLE)
self.assertNotIn("# example: {", output)
self.assertNotIn("# example: {", output)

def test_character_encoding(self):
output = generate_schema_types(CHARACTER_SCHEMA, "Dict")
expected = 'from typing import TypedDict\n\n\nclass Dict(TypedDict, total=False):\n CHARACTER_SCHEMA_NAME: str\n """ This is — “bad”, right? """\n\n'
self.assertEqual(output, expected)