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
40 changes: 37 additions & 3 deletions polyapi/poly_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@

def generate_schemas(specs: List[SchemaSpecDto], limit_ids: List[str] = None):
failed_schemas = []
successful_schemas = []
if limit_ids:
for spec in specs:
if spec["id"] in limit_ids:
try:
create_schema(spec)
successful_schemas.append(f"{spec.get('context', 'unknown')}.{spec.get('name', 'unknown')}")
except Exception as e:
schema_path = f"{spec.get('context', 'unknown')}.{spec.get('name', 'unknown')}"
schema_id = spec.get('id', 'unknown')
Expand All @@ -40,6 +42,7 @@ def generate_schemas(specs: List[SchemaSpecDto], limit_ids: List[str] = None):
for spec in specs:
try:
create_schema(spec)
successful_schemas.append(f"{spec.get('context', 'unknown')}.{spec.get('name', 'unknown')}")
except Exception as e:
schema_path = f"{spec.get('context', 'unknown')}.{spec.get('name', 'unknown')}"
schema_id = spec.get('id', 'unknown')
Expand All @@ -51,6 +54,37 @@ def generate_schemas(specs: List[SchemaSpecDto], limit_ids: List[str] = None):
logging.warning(f"WARNING: {len(failed_schemas)} schema(s) failed to generate:")
for failed_schema in failed_schemas:
logging.warning(f" - {failed_schema}")
logging.warning(f"Successfully generated {len(successful_schemas)} schema(s)")


def validate_schema_content(schema_content: str, schema_name: str) -> bool:
"""
Validate that the schema content is meaningful and not just imports.
Returns True if the schema is valid, False otherwise.
"""
if not schema_content or not schema_content.strip():
logging.debug(f"Schema {schema_name} failed validation: Empty content")
return False

lines = schema_content.strip().split('\n')

# Check if the content has any actual class definitions or type aliases
has_class_definition = any(line.strip().startswith('class ') for line in lines)
has_type_alias = any(schema_name in line and '=' in line and not line.strip().startswith('#') for line in lines)

# Check if it's essentially just imports (less than 5 lines and no meaningful definitions)
meaningful_lines = [line for line in lines if line.strip() and not line.strip().startswith('from ') and not line.strip().startswith('import ') and not line.strip().startswith('#')]

# Enhanced logging for debugging
if not (has_class_definition or has_type_alias) or len(meaningful_lines) < 1:
# Determine the specific reason for failure
if len(meaningful_lines) == 0:
logging.debug(f"Schema {schema_name} failed validation: No meaningful content (only imports) - likely empty object or unresolved reference")
elif not has_class_definition and not has_type_alias:
logging.debug(f"Schema {schema_name} failed validation: No class definition or type alias found")
return False

return True


def add_schema_file(
Expand All @@ -75,9 +109,9 @@ def add_schema_file(

schema_defs = render_poly_schema(spec)

if not schema_defs:
# If render_poly_schema failed and returned empty string, don't create any files
raise Exception("Schema rendering failed - empty schema content returned")
# Validate schema content before proceeding
if not validate_schema_content(schema_defs, schema_name):
raise Exception(f"Schema rendering failed or produced invalid content for {schema_name}")

# Prepare all content first before writing any files
schema_namespace = to_func_namespace(schema_name)
Expand Down
16 changes: 15 additions & 1 deletion polyapi/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,26 @@ def generate_schema_types(input_data: Dict, root=None):
# Regex to match everything between "# example: {\n" and "^}$"
MALFORMED_EXAMPLES_PATTERN = re.compile(r"# example: \{\n.*?^\}$", flags=re.DOTALL | re.MULTILINE)

# Regex to fix invalid escape sequences in docstrings
INVALID_ESCAPE_PATTERNS = [
# Fix "\ " (backslash space) which is not a valid escape sequence
(re.compile(r'\\(\s)', re.DOTALL), r'\1'),
# Fix other common invalid escape sequences in docstrings
(re.compile(r'\\([^nrtbfav"\'\\])', re.DOTALL), r'\\\\\1'),
]


def clean_malformed_examples(example: str) -> str:
""" there is a bug in the `jsonschmea_gentypes` library where if an example from a jsonchema is an object,
it will break the code because the object won't be properly commented out
it will break the code because the object won't be properly commented out. Also fixes invalid escape sequences.
"""
# Remove malformed examples
cleaned_example = MALFORMED_EXAMPLES_PATTERN.sub("", example)

# Fix invalid escape sequences in docstrings
for pattern, replacement in INVALID_ESCAPE_PATTERNS:
cleaned_example = pattern.sub(replacement, cleaned_example)

return cleaned_example


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.dev9"
version = "0.3.8.dev10"
description = "The Python Client for PolyAPI, the IPaaS by Developers for Developers"
authors = [{ name = "Dan Fellin", email = "dan@polyapi.io" }]
dependencies = [
Expand Down