OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.75k stars 6.56k forks source link

[BUG] python-flask server ImportError exception at launch #17063

Open d3vopz-net opened 11 months ago

d3vopz-net commented 11 months ago

Bug Report Checklist

Description
Exception has occurred: ImportError
cannot import name 'FlaskJSONEncoder' from 'connexion.apps' (/home/d3vopz/openapi/.venv/lib/python3.11/site-packages/connexion/apps/__init__.py)
  File "/home/d3vopz/openapi/test/resources/spec/out/python-flask-server/openapi/encoder.py", line 1, in <module>
    from connexion.apps import FlaskJSONEncoder
  File "/home/d3vopz/openapi/test/resources/spec/out/python-flask-server/openapi/__main__.py", line 5, in <module>
    from openapi import encoder
ImportError: cannot import name 'FlaskJSONEncoder' from 'connexion.apps' (/home/d3vopz/openapi/.venv/lib/python3.11/site-packages/connexion/apps/__init__.py)
openapi-generator version

7.0.1

OpenAPI declaration file content or url
Generation Details

api: python-flask server pip3 install -r python-flask-server/requirements.txt

Steps to reproduce

curl api unzip pip3 install -r python-flask-server/requirements.txt run in vscode

        "configurations": [
            {
                "name": "Python: oag",
                "type": "python",
                "request": "launch",
                "program": "/home/d3vopz/openapi/test/resources/spec/out/python-flask-server/openapi/__main__.py",
                "console": "integratedTerminal",
                "cwd": "/home/d3vopz/openapi",
                "env": {
                    "PYTHONPATH": "/home/d3vopz/openapi/test/python/src:/home/d3vopz/etl/test/src:/home/d3vopz/openapi/test/resources/spec/out/python-flask-server"
                },
                "envFile": "/home/d3vopz/openapi/.env",
                "args":["--resource-path","/home/d3vopz/etl/resources/inject"]
            }
        ]

cd /home/d3vopz/openapi ; /usr/bin/env /home/d3vopz/openapi/.venv/bin/python /home/d3vopz/.vscode-server/extensions/ms-python.python-2023.20.0/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher 56187 -- /home/d3vopz/openapi/test/resources/spec/out/python-flask-server/openapi/__main__.py --resource-path /home/d3vopz/etl/resources/inject

Related issues/PRs
Suggest a fix
MarkSchmidts commented 11 months ago

I have a very similiar problem

 File "/app/service/app.py", line 10, in <module>
    from service.generated.encoder import JSONEncoder
  File "/app/service/generated/encoder.py", line 1, in <module>
    from connexion.apps.flask_app import FlaskJSONEncoder
ModuleNotFoundError: No module named 'connexion.apps.flask_app'

this is from my app.py

from service.generated.encoder import JSONEncoder
(...)
def create_app() -> connexion.FlaskApp:
    (...)
    connexion_app = connexion.FlaskApp(__name__, specification_dir="..")
    connexion_app.add_api("openapi.yaml")
    (...)
    connexion_app.app.json_encoder = JSONEncoder

This is the generated code beeing generated: accountinfo/service/generated/encoder.py

from connexion.apps.flask_app import FlaskJSONEncoder
import six

from service.generated.models.base_model_ import Model

class JSONEncoder(FlaskJSONEncoder):
    include_nulls = False

    def default(self, o):
        if isinstance(o, Model):
            dikt = {}
            for attr, _ in six.iteritems(o.openapi_types):
                value = getattr(o, attr)
                if value is None and not self.include_nulls:
                    continue
                attr = o.attribute_map[attr]
                dikt[attr] = value
            return dikt
        return FlaskJSONEncoder.default(self, o)

Maybe it would work if the generator would generate the same but use the standard JSON encoder from connexion? something like this:

from connexion.jsonifier import JSONEncoder
import six

from service.generated.models.base_model_ import Model

class JSONEncoder(JSONEncoder):
    include_nulls = False

    def default(self, o):
        if isinstance(o, Model):
            dikt = {}
            for attr, _ in six.iteritems(o.openapi_types):
                value = getattr(o, attr)
                if value is None and not self.include_nulls:
                    continue
                attr = o.attribute_map[attr]
                dikt[attr] = value
            return dikt
        return JSONEncoder.default(self, o)
MarkSchmidts commented 11 months ago

Actually the issue is probably solved by using FlaskJSONProvider. Flask changed with a version update.

Here's the current jsonproviderClass: https://github.com/spec-first/connexion/blob/b244d809089eb584ce3e9661d619ed5564ca4103/connexion/frameworks/flask.py#L123

I think the current code that the generator generates is incorrect and should be replaced.

MarkSchmidts commented 11 months ago

This might fix it https://github.com/OpenAPITools/openapi-generator/pull/17144/files

But I can't get the dev setup running. If someone can check the PR, this issue could be fixed.

d3vopz-net commented 10 months ago

checked . its still in 7.2.0 :(

checked out https://github.com/MarkSchmidts/openapi-generator/tree/master

but

Traceback (most recent call last):
  File "/usr/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/home/ripper/gitrepo/gitlab.com/d3vopz-public/tools/d3vopz-openapi.git/test/resources/spec/out/python-flask-server/openapi/__main__.py", line 5, in <module>
    from openapi import encoder
  File "/home/ripper/gitrepo/gitlab.com/d3vopz-public/tools/d3vopz-openapi.git/test/resources/spec/out/python-flask-server/openapi/encoder.py", line 1, in <module>
    from connexion.frameworks.flask import FlaskJSONProvider
ModuleNotFoundError: No module named 'connexion.frameworks'

Found also this https://github.com/swagger-api/swagger-codegen/issues/12278

dfsp-spirit commented 10 months ago

Is there any workaround for this, or should we try to monkey-patch with the patch @MarkSchmidts suggested for now?

d3vopz-net commented 10 months ago

Is there any workaround for this, or should we try to monkey-patch with the patch @MarkSchmidts suggested for now?

I got no working workaround. sorry.

EingeoelterBolt commented 9 months ago

A workaround that helped me to run the generated server was to remove these lines from the requirements.txt file:

connexion[swagger-ui] >= 2.6.0; python_version>="3.6"
# 2.3 is the last version that supports python 3.4-3.5
connexion[swagger-ui] <= 2.3.0; python_version=="3.5" or python_version=="3.4"
# connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug
# we must peg werkzeug versions below to fix connexion
# https://github.com/zalando/connexion/pull/1044
werkzeug == 0.16.1; python_version=="3.5" or python_version=="3.4"

and replace it with:

connexion[swagger-ui]==2.14.2

AND

replace in the Dockerfile the line

FROM python:3-alpine

to

FROM python:3.11.5
wing328 commented 9 months ago

i think this has been fixed in the latest master. please pull the latest or use the snapshot JAR to give it a try.

d3vopz-net commented 9 months ago

i think this has been fixed in the latest master. please pull the latest or use the snapshot JAR to give it a try.

Idk why i got now this issue when starting the generator:

...
Error: Unable to initialize main class org.openapitools.codegen.OpenAPIGenerator
Caused by: java.lang.NoClassDefFoundError: io/airlift/airline/ParseArgumentsUnexpectedException

Its mentioned in modules/openapi-generator-cli/pom.xml but does not exist in modules/openapi-generator-cli/target folder.

Im sry, but is it another issue or my setup?

best

wing328 commented 9 months ago

which JDK version are you using?

the latest 7.x requires JDK11 +

d3vopz-net commented 9 months ago

openjdk 17.0.9 2023-10-17 OpenJDK Runtime Environment (build 17.0.9+9-Debian-1deb12u1) OpenJDK 64-Bit Server VM (build 17.0.9+9-Debian-1deb12u1, mixed mode, sharing)

Am So., 28. Jan. 2024 um 15:08 Uhr schrieb William Cheng < @.***>:

which JDK version are you using?

the latest 7.x requires JDK11 +

— Reply to this email directly, view it on GitHub https://github.com/OpenAPITools/openapi-generator/issues/17063#issuecomment-1913608981, or unsubscribe https://github.com/notifications/unsubscribe-auth/ALQLAUV2Q73GY27DTLDHE23YQZLVHAVCNFSM6AAAAAA7J4JEWCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMJTGYYDQOJYGE . You are receiving this because you authored the thread.Message ID: @.***>

wing328 commented 9 months ago

can you please PM me via Slack this week to help you troubleshoot the issue?

tomtucker commented 8 months ago

I have followed the workaround suggested by @EingeoelterBolt above and removed, recreated, and activated my virtual environment (python -m venv venv) to make sure there are no collisions..

Now I receive the error:

(venv) 🐉 >python3 -m openapi_server
Failed to add operation for POST /capture
Traceback (most recent call last):
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/apis/abstract.py", line 222, in add_paths
    self.add_operation(path, method)
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/apis/abstract.py", line 175, in add_operation
    operation = make_operation(
                ^^^^^^^^^^^^^^^
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/operations/__init__.py", line 16, in make_operation
    return spec.operation_cls.from_spec(spec, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/operations/openapi.py", line 134, in from_spec
    return cls(
           ^^^^
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/operations/openapi.py", line 81, in __init__
    super().__init__(
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/operations/abstract.py", line 101, in __init__
    self._resolution = resolver.resolve(self)
                       ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/resolver.py", line 47, in resolve
    return Resolution(self.resolve_function_from_operation_id(operation_id), operation_id)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/resolver.py", line 71, in resolve_function_from_operation_id
    raise ResolverError(msg, sys.exc_info())
connexion.exceptions.ResolverError: <ResolverError: Cannot resolve operationId "swagger_server.controllers.default_controller.capture_transaction"! Import error was "No module named 'swagger_server'">

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/openapi_server/__main__.py", line 19, in <module>
    main()
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/openapi_server/__main__.py", line 11, in main
    app.add_api('openapi.yaml',
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/apps/flask_app.py", line 74, in add_api
    api = super().add_api(specification, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/apps/abstract.py", line 149, in add_api
    api = self.api_cls(specification,
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/apis/abstract.py", line 119, in __init__
    self.add_paths()
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/apis/abstract.py", line 229, in add_paths
    self._handle_add_operation_error(path, method, err.exc_info)
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/apis/abstract.py", line 244, in _handle_add_operation_error
    raise value.with_traceback(traceback)
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/resolver.py", line 68, in resolve_function_from_operation_id
    return self.function_resolver(operation_id)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ttucker/dev/python/PaymentsServer/openapi-generator/flask2/venv/lib/python3.11/site-packages/connexion/utils.py", line 116, in get_function_from_name
    module = importlib.import_module(module_name)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ttucker/.pyenv/versions/3.11.7/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1140, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'swagger_server'
tomtucker commented 8 months ago

I resolved the above issue by downgrading the python virtual environment for server execution to python 3.9 instead of python 3.11.

Still using @EingeoelterBolt's changes to requirements.txt