Skip to content
GitLab
    • Explore Projects Groups Snippets
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • O openapi-generator
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 3,476
    • Issues 3,476
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 402
    • Merge requests 402
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • OpenAPI Tools
  • openapi-generator
  • Merge requests
  • !5094

[python] Cleanup ThreadPool with atexit rather than __del__

  • Review changes

  • Download
  • Email patches
  • Plain diff
Merged Administrator requested to merge github/fork/fabianvf/python3-del-hang into master 5 years ago
  • Overview 0
  • Commits 6
  • Pipelines 0
  • Changes 58

Created by: fabianvf

This removes the __del__ function from the generated Python client, and replaces it with a cleanup function. When a ThreadPool is created, the cleanup function is registered with the atexit module.

This fixes #5093 (closed), where the API client could hang indefinitely at garbage collection.

Related to https://github.com/swagger-api/swagger-codegen/pull/10002

PR checklist

  • Read the contribution guidelines.
  • If contributing template-only or documentation-only changes which will change sample output, build the project before.
  • Run the shell script(s) under ./bin/ (or Windows batch scripts under.\bin\windows) to update Petstore samples related to your fix. This is important, as CI jobs will verify all generator outputs of your HEAD commit, and these must match the expectations made by your contribution. You only need to run ./bin/{LANG}-petstore.sh, ./bin/openapi3/{LANG}-petstore.sh if updating the code or mustache templates for a language ({LANG}) (e.g. php, ruby, python, etc).
  • File the PR against the correct branch: master, 4.3.x, 5.0.x. Default: master.
  • Copy the technical committee to review the pull request if your PR is targeting a particular programming language.

cc @taxpon @frol @mbohlool @cbornet @kenjones-cisco @tomplus @Jyhess @slash-arun @spacether

Compare
  • master (base)

and
  • latest version
    1d179e03
    6 commits, 2 years ago

58 files
+ 2968
- 2382

    Preferences

    File browser
    Compare changes
modules/openapi-gen‎erator/…/…/…/python‎
python-ex‎perimental‎
README_comm‎on.mustache‎ +13 -11
api_clien‎t.mustache‎ +11 -1
api_doc_exam‎ple.mustache‎ +45 -41
api_clien‎t.mustache‎ +11 -1
api_doc_exam‎ple.mustache‎ +18 -14
common_READ‎ME.mustache‎ +13 -11
sam‎ples‎
client/‎petstore‎
pyt‎hon‎
do‎cs‎
AnotherF‎akeApi.md‎ +12 -10
FakeA‎pi.md‎ +145 -117
FakeClassname‎Tags123Api.md‎ +12 -10
PetA‎pi.md‎ +100 -82
Store‎Api.md‎ +46 -38
UserA‎pi.md‎ +87 -71
petsto‎re_api‎
api_cl‎ient.py‎ +11 -1
te‎sts‎
test_api_‎client.py‎ +16 -0
READ‎ME.md‎ +13 -11
python-‎asyncio‎
do‎cs‎
AnotherF‎akeApi.md‎ +12 -10
FakeA‎pi.md‎ +145 -117
FakeClassname‎Tags123Api.md‎ +12 -10
PetA‎pi.md‎ +100 -82
Store‎Api.md‎ +46 -38
UserA‎pi.md‎ +87 -71
petsto‎re_api‎
api_cl‎ient.py‎ +11 -1
READ‎ME.md‎ +13 -11
python-ex‎perimental‎
do‎cs‎
AnotherF‎akeApi.md‎ +13 -11
FakeA‎pi.md‎ +205 -175
FakeClassname‎Tags123Api.md‎ +13 -11
PetA‎pi.md‎ +150 -132
Store‎Api.md‎ +50 -42
UserA‎pi.md‎ +99 -83
petsto‎re_api‎
api_cl‎ient.py‎ +11 -1
te‎sts‎
test_api_‎client.py‎ +16 -0
READ‎ME.md‎ +13 -11
python-‎tornado‎
do‎cs‎
AnotherF‎akeApi.md‎ +12 -10
FakeA‎pi.md‎ +145 -117
FakeClassname‎Tags123Api.md‎ +12 -10
PetA‎pi.md‎ +100 -82
Store‎Api.md‎ +46 -38
UserA‎pi.md‎ +87 -71
petsto‎re_api‎
api_cl‎ient.py‎ +11 -1
READ‎ME.md‎ +13 -11
openapi3/cli‎ent/petstore‎
pyt‎hon‎
do‎cs‎
AnotherF‎akeApi.md‎ +12 -10
Defaul‎tApi.md‎ +10 -8
FakeA‎pi.md‎ +145 -117
FakeClassname‎Tags123Api.md‎ +12 -10
PetA‎pi.md‎ +100 -82
Store‎Api.md‎ +46 -38
UserA‎pi.md‎ +87 -71
petsto‎re_api‎
api_cl‎ient.py‎ +11 -1
READ‎ME.md‎ +13 -11
python-ex‎perimental‎
do‎cs‎
AnotherF‎akeApi.md‎ +13 -11
Defaul‎tApi.md‎ +11 -9
FakeA‎pi.md‎ +197 -169
FakeClassname‎Tags123Api.md‎ +13 -11
PetA‎pi.md‎ +150 -132
Store‎Api.md‎ +50 -42
UserA‎pi.md‎ +99 -83
petsto‎re_api‎
api_cl‎ient.py‎ +11 -1
READ‎ME.md‎ +13 -11
modules/openapi-generator/src/main/resources/python/python-experimental/README_common.mustache
+ 13
- 11
  • View file @ 1d179e03

  • Edit in single-file editor

  • Open in Web IDE


@@ -7,18 +7,20 @@ from pprint import pprint
{{> python_doc_auth_partial}}
# Defining host is optional and default to {{{basePath}}}
configuration.host = "{{{basePath}}}"
# Create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}({{{packageName}}}.ApiClient(configuration))
{{#allParams}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}
{{/allParams}}
# Enter a context with an instance of the API client
with {{{packageName}}}.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}(api_client)
{{#allParams}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}
{{/allParams}}
try:
{{#summary}} # {{{.}}}
{{/summary}} {{#returnType}}api_response = {{/returnType}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}
pprint(api_response){{/returnType}}
except {{{packageName}}}.ApiException as e:
print("Exception when calling {{classname}}->{{operationId}}: %s\n" % e)
{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}}
try:
{{#summary}} # {{{.}}}
{{/summary}} {{#returnType}}api_response = {{/returnType}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}
pprint(api_response){{/returnType}}
except {{{packageName}}}.ApiException as e:
print("Exception when calling {{classname}}->{{operationId}}: %s\n" % e)
{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}}
```
## Documentation for API Endpoints
modules/openapi-generator/src/main/resources/python/python-experimental/api_client.mustache
+ 11
- 1
  • View file @ 1d179e03

  • Edit in single-file editor

  • Open in Web IDE


@@ -3,6 +3,7 @@
from __future__ import absolute_import
import json
import atexit
import mimetypes
from multiprocessing.pool import ThreadPool
import os
@@ -74,11 +75,19 @@ class ApiClient(object):
# Set default User-Agent.
self.user_agent = '{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{packageVersion}}}/python{{/httpUserAgent}}'
def __del__(self):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
def close(self):
if self._pool:
self._pool.close()
self._pool.join()
self._pool = None
if hasattr(atexit, 'unregister'):
atexit.unregister(self.close)
@property
def pool(self):
@@ -86,6 +95,7 @@ class ApiClient(object):
avoids instantiating unused threadpool for blocking clients.
"""
if self._pool is None:
atexit.register(self.close)
self._pool = ThreadPool(self.pool_threads)
return self._pool
modules/openapi-generator/src/main/resources/python/python-experimental/api_doc_example.mustache
+ 45
- 41
  • View file @ 1d179e03

  • Edit in single-file editor

  • Open in Web IDE


@@ -7,51 +7,55 @@ from pprint import pprint
{{#hasAuthMethods}}
# Defining host is optional and default to {{{basePath}}}
configuration.host = "{{{basePath}}}"
# Create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}({{{packageName}}}.ApiClient(configuration))
# Enter a context with an instance of the API client
with {{{packageName}}}.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}(api_client)
{{/hasAuthMethods}}
{{^hasAuthMethods}}
# Create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}()
# Enter a context with an instance of the API client
with {{{packageName}}}.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}(api_client)
{{/hasAuthMethods}}
{{#requiredParams}}{{^defaultValue}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}
{{/defaultValue}}{{/requiredParams}}{{#optionalParams}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} if omitted the server will use the default value of {{{defaultValue}}}{{/defaultValue}}
{{/optionalParams}}
{{#requiredParams}}
{{^hasMore}}
{{#requiredParams}}{{^defaultValue}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}
{{/defaultValue}}{{/requiredParams}}{{#optionalParams}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} if omitted the server will use the default value of {{{defaultValue}}}{{/defaultValue}}
{{/optionalParams}}
{{#requiredParams}}
{{^hasMore}}
# example passing only required values which don't have defaults set
try:
{{#summary}} # {{{.}}}
{{/summary}} {{#returnType}}api_response = {{/returnType}}api_instance.{{{operationId}}}({{#requiredParams}}{{^defaultValue}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/defaultValue}}{{/requiredParams}}){{#returnType}}
pprint(api_response){{/returnType}}
except {{{packageName}}}.ApiException as e:
print("Exception when calling {{classname}}->{{operationId}}: %s\n" % e)
{{/hasMore}}
{{/requiredParams}}
{{#optionalParams}}
{{^hasMore}}
# example passing only required values which don't have defaults set
try:
{{#summary}} # {{{.}}}
{{/summary}} {{#returnType}}api_response = {{/returnType}}api_instance.{{{operationId}}}({{#requiredParams}}{{^defaultValue}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/defaultValue}}{{/requiredParams}}){{#returnType}}
pprint(api_response){{/returnType}}
except {{{packageName}}}.ApiException as e:
print("Exception when calling {{classname}}->{{operationId}}: %s\n" % e)
{{/hasMore}}
{{/requiredParams}}
{{#optionalParams}}
{{^hasMore}}
# example passing only required values which don't have defaults set
# and optional values
try:
{{#summary}} # {{{.}}}
{{/summary}} {{#returnType}}api_response = {{/returnType}}api_instance.{{{operationId}}}({{#requiredParams}}{{^defaultValue}}{{paramName}}, {{/defaultValue}}{{/requiredParams}}{{#optionalParams}}{{paramName}}={{paramName}}{{#hasMore}}, {{/hasMore}}{{/optionalParams}}){{#returnType}}
pprint(api_response){{/returnType}}
except {{{packageName}}}.ApiException as e:
print("Exception when calling {{classname}}->{{operationId}}: %s\n" % e)
{{/hasMore}}
{{/optionalParams}}
{{^requiredParams}}
{{^optionalParams}}
# example passing only required values which don't have defaults set
# and optional values
try:
{{#summary}} # {{{.}}}
{{/summary}} {{#returnType}}api_response = {{/returnType}}api_instance.{{{operationId}}}({{#requiredParams}}{{^defaultValue}}{{paramName}}, {{/defaultValue}}{{/requiredParams}}{{#optionalParams}}{{paramName}}={{paramName}}{{#hasMore}}, {{/hasMore}}{{/optionalParams}}){{#returnType}}
pprint(api_response){{/returnType}}
except {{{packageName}}}.ApiException as e:
print("Exception when calling {{classname}}->{{operationId}}: %s\n" % e)
{{/hasMore}}
{{/optionalParams}}
{{^requiredParams}}
{{^optionalParams}}
# example, this endpoint has no required or optional parameters
try:
{{#summary}} # {{{.}}}
{{/summary}} {{#returnType}}api_response = {{/returnType}}api_instance.{{{operationId}}}(){{#returnType}}
pprint(api_response){{/returnType}}
except {{{packageName}}}.ApiException as e:
print("Exception when calling {{classname}}->{{operationId}}: %s\n" % e)
{{/optionalParams}}
{{/requiredParams}}
# example, this endpoint has no required or optional parameters
try:
{{#summary}} # {{{.}}}
{{/summary}} {{#returnType}}api_response = {{/returnType}}api_instance.{{{operationId}}}(){{#returnType}}
pprint(api_response){{/returnType}}
except {{{packageName}}}.ApiException as e:
print("Exception when calling {{classname}}->{{operationId}}: %s\n" % e)
{{/optionalParams}}
{{/requiredParams}}
```
modules/openapi-generator/src/main/resources/python/api_client.mustache
+ 11
- 1
  • View file @ 1d179e03

  • Edit in single-file editor

  • Open in Web IDE


@@ -2,6 +2,7 @@
{{>partial_header}}
from __future__ import absolute_import
import atexit
import datetime
from dateutil.parser import parse
import json
@@ -75,11 +76,19 @@ class ApiClient(object):
self.user_agent = '{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{packageVersion}}}/python{{/httpUserAgent}}'
self.client_side_validation = configuration.client_side_validation
def __del__(self):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
def close(self):
if self._pool:
self._pool.close()
self._pool.join()
self._pool = None
if hasattr(atexit, 'unregister'):
atexit.unregister(self.close)
@property
def pool(self):
@@ -87,6 +96,7 @@ class ApiClient(object):
avoids instantiating unused threadpool for blocking clients.
"""
if self._pool is None:
atexit.register(self.close)
self._pool = ThreadPool(self.pool_threads)
return self._pool
modules/openapi-generator/src/main/resources/python/api_doc_example.mustache
+ 18
- 14
  • View file @ 1d179e03

  • Edit in single-file editor

  • Open in Web IDE


@@ -8,22 +8,26 @@ from pprint import pprint
{{#hasAuthMethods}}
# Defining host is optional and default to {{{basePath}}}
configuration.host = "{{{basePath}}}"
# Create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}({{{packageName}}}.ApiClient(configuration))
{{#allParams}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}
{{/allParams}}
# Enter a context with an instance of the API client
with {{{packageName}}}.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}(api_client)
{{#allParams}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}
{{/allParams}}
{{/hasAuthMethods}}
{{^hasAuthMethods}}
# Create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}()
{{#allParams}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}
{{/allParams}}
# Enter a context with an instance of the API client
with {{{packageName}}}.ApiClient() as api_client:
# Create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}(api_client)
{{#allParams}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}
{{/allParams}}
{{/hasAuthMethods}}
try:
{{#summary}} # {{{.}}}
{{/summary}} {{#returnType}}api_response = {{/returnType}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}
pprint(api_response){{/returnType}}
except ApiException as e:
print("Exception when calling {{classname}}->{{operationId}}: %s\n" % e)
try:
{{#summary}} # {{{.}}}
{{/summary}} {{#returnType}}api_response = {{/returnType}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}
pprint(api_response){{/returnType}}
except ApiException as e:
print("Exception when calling {{classname}}->{{operationId}}: %s\n" % e)
```
0 Assignees
None
Assign to
0 Reviewers
None
Request review from
Labels
1
Client: Python
1
Client: Python
    Assign labels
  • Manage project labels

Milestone
4.2.3
4.2.3 (expired)
None
Time tracking
No estimate or time spent
Lock merge request
Unlocked
2
2 participants
Administrator
Tomasz Prus
Reference: OpenAPITools/openapi-generator!5094
Source branch: github/fork/fabianvf/python3-del-hang

Menu

Explore Projects Groups Snippets