gnpy-api 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. gnpy_api-0.0.0.dist-info/METADATA +78 -0
  2. gnpy_api-0.0.0.dist-info/RECORD +40 -0
  3. gnpy_api-0.0.0.dist-info/WHEEL +5 -0
  4. gnpy_api-0.0.0.dist-info/licenses/AUTHORS +1 -0
  5. gnpy_api-0.0.0.dist-info/licenses/LICENSE +28 -0
  6. gnpy_api-0.0.0.dist-info/pbr.json +1 -0
  7. gnpy_api-0.0.0.dist-info/top_level.txt +1 -0
  8. gnpyapi/__init__.py +0 -0
  9. gnpyapi/core/__init__.py +12 -0
  10. gnpyapi/core/exception/__init__.py +1 -0
  11. gnpyapi/core/exception/config_error.py +14 -0
  12. gnpyapi/core/exception/equipment_error.py +14 -0
  13. gnpyapi/core/exception/exception_handler.py +34 -0
  14. gnpyapi/core/exception/path_computation_error.py +14 -0
  15. gnpyapi/core/exception/topology_error.py +13 -0
  16. gnpyapi/core/model/__init__.py +1 -0
  17. gnpyapi/core/model/error.py +17 -0
  18. gnpyapi/core/model/result.py +8 -0
  19. gnpyapi/core/route/__init__.py +1 -0
  20. gnpyapi/core/route/path_request_route.py +28 -0
  21. gnpyapi/core/route/status_route.py +8 -0
  22. gnpyapi/core/service/__init__.py +1 -0
  23. gnpyapi/core/service/config_service.py +4 -0
  24. gnpyapi/core/service/equipment_service.py +5 -0
  25. gnpyapi/core/service/path_request_service.py +36 -0
  26. gnpyapi/core/service/topology_service.py +5 -0
  27. gnpyapi/exampledata/planning_demand_example.json +1578 -0
  28. gnpyapi/tools/__init__.py +0 -0
  29. gnpyapi/yang/gnpy-api@2021-01-06.yang +81 -0
  30. gnpyapi/yang/gnpy-eqpt-config@2020-10-22.yang +442 -0
  31. gnpyapi/yang/gnpy-eqpt-config@2025-01-20.yang +770 -0
  32. gnpyapi/yang/gnpy-network-topology@2020-10-22.yang +300 -0
  33. gnpyapi/yang/gnpy-network-topology@2025-01-20.yang +594 -0
  34. gnpyapi/yang/gnpy-path-computation-simplified@2020-10-22.yang +559 -0
  35. gnpyapi/yang/gnpy-path-computation@2025-01-21.yang +632 -0
  36. gnpyapi/yang/ietf-layer0-types@2024-03-04.yang +2247 -0
  37. gnpyapi/yang/ietf-optical-impairment-topology@2024-05-21.yang +1776 -0
  38. gnpyapi/yang/ietf-routing-types@2017-12-04.yang +771 -0
  39. gnpyapi/yang/ietf-te-topology@2020-08-06.yang +1952 -0
  40. gnpyapi/yang/ietf-te-types@2022-10-21.yang +3458 -0
@@ -0,0 +1,78 @@
1
+ Metadata-Version: 2.4
2
+ Name: gnpy-api
3
+ Version: 0.0.0
4
+ Home-page: https://github.com/Telecominfraproject/oopt-gnpy-api
5
+ Author: Telecom Infra Project
6
+ Author-email: adamico@nec-labs.com
7
+ License: BSD-3-Clause
8
+ Project-URL: Bug Tracker, https://github.com/Telecominfraproject/oopt-gnpy-api/issues
9
+ Project-URL: Documentation, https://gnpyapi.readthedocs.io/
10
+ Keywords: optics,network,fiber,communication,route,planning,optimization,api
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Intended Audience :: Telecommunications Industry
15
+ Classifier: License :: OSI Approved :: BSD License
16
+ Classifier: Natural Language :: English
17
+ Classifier: Programming Language :: Python
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: Implementation :: CPython
25
+ Classifier: Topic :: Scientific/Engineering
26
+ Classifier: Topic :: Scientific/Engineering :: Physics
27
+ Classifier: Topic :: System :: Networking
28
+ Requires-Python: >=3.8
29
+ Description-Content-Type: text/markdown; variant=GFM
30
+ License-File: LICENSE
31
+ License-File: AUTHORS
32
+ Requires-Dist: setuptools
33
+ Requires-Dist: gnpy==2.12.1
34
+ Requires-Dist: flask>=1.1.2
35
+ Requires-Dist: Flask-Injector
36
+ Requires-Dist: pyopenssl==25.0.0
37
+ Provides-Extra: tests
38
+ Requires-Dist: build<2,>=1.0.3; extra == "tests"
39
+ Requires-Dist: pytest<8,>=7.4.3; extra == "tests"
40
+ Requires-Dist: pyang<3,>=2.6.1; extra == "tests"
41
+ Provides-Extra: docs
42
+ Requires-Dist: alabaster<1,>=0.7.12; extra == "docs"
43
+ Requires-Dist: docutils<1,>=0.17.1; extra == "docs"
44
+ Requires-Dist: myst-parser<1,>=0.16.1; extra == "docs"
45
+ Requires-Dist: Pygments<3,>=2.11.2; extra == "docs"
46
+ Requires-Dist: rstcheck; extra == "docs"
47
+ Requires-Dist: Sphinx<6,>=5.3.0; extra == "docs"
48
+ Requires-Dist: sphinxcontrib-bibtex<3,>=2.4.1; extra == "docs"
49
+ Dynamic: author
50
+ Dynamic: author-email
51
+ Dynamic: classifier
52
+ Dynamic: description
53
+ Dynamic: description-content-type
54
+ Dynamic: home-page
55
+ Dynamic: keywords
56
+ Dynamic: license
57
+ Dynamic: license-file
58
+ Dynamic: project-url
59
+ Dynamic: requires-python
60
+
61
+ # GNPy API
62
+ [![Python versions](https://img.shields.io/pypi/pyversions/gnpy)](https://pypi.org/project/gnpy/)
63
+
64
+ REST API (experimental)
65
+ -----------------------
66
+ ``gnpyapi`` provides an experimental api for requesting several paths at once. It is based on Flask server.
67
+ You can run it through command line or Docker.
68
+
69
+
70
+
71
+ $ curl --location 'http://localhost:8080/api/v1/path-request' --header 'Content-Type: application/json' --data @gnpyapi/exampledata/planning_demand_example.json
72
+
73
+ TODO: api documentation, unit tests, real WSGI server with trusted certificates
74
+
75
+ ## Quick Start
76
+
77
+ tbd
78
+
@@ -0,0 +1,40 @@
1
+ gnpy_api-0.0.0.dist-info/licenses/AUTHORS,sha256=VP1D1A_-JYIc3kvJtwEUQkkMgjOl09IgrTH1GyzxjPI,36
2
+ gnpy_api-0.0.0.dist-info/licenses/LICENSE,sha256=cEDonVncyb0p8F7f57_25_rcOTxaWOPY8TTri-CkNTc,1508
3
+ gnpyapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ gnpyapi/core/__init__.py,sha256=QQOebt2JqesPW7YKguN2bRa_WifHtPHSAMtQXo8tgms,241
5
+ gnpyapi/core/exception/__init__.py,sha256=Hc0g8HMsO3WRghOQdRyaLC9zjc2pRrVAB24uYcIfmH0,16
6
+ gnpyapi/core/exception/config_error.py,sha256=8rBUTNs1oflTZPdbUc4p1fntD6NjRQbdvQlx-qTgES0,286
7
+ gnpyapi/core/exception/equipment_error.py,sha256=16XowWrrwGDIquqxYmM7oUZPrsjxd3TEiPGqs5WHiSE,280
8
+ gnpyapi/core/exception/exception_handler.py,sha256=6SKxhqOjH0lx-sFw3ljPt9blODH7xUtgnFLwdH3pmPM,1069
9
+ gnpyapi/core/exception/path_computation_error.py,sha256=r2NXIzOWktLwilqSKM-S-YCblry05VEkJTgSdbPxNhA,299
10
+ gnpyapi/core/exception/topology_error.py,sha256=WlY97n8uba5fB73Eu2nDpEyN6v5g1EJ-gDL_9s_7lxI,277
11
+ gnpyapi/core/model/__init__.py,sha256=Hc0g8HMsO3WRghOQdRyaLC9zjc2pRrVAB24uYcIfmH0,16
12
+ gnpyapi/core/model/error.py,sha256=zAIahu9qwIsChuhrnHWpQee3noNDcCLPQbKxmE0RFMM,481
13
+ gnpyapi/core/model/result.py,sha256=Te-u9PuZY4pdle39h03IT-6jTWYDwdVi9r5w-opyb0Y,173
14
+ gnpyapi/core/route/__init__.py,sha256=Hc0g8HMsO3WRghOQdRyaLC9zjc2pRrVAB24uYcIfmH0,16
15
+ gnpyapi/core/route/path_request_route.py,sha256=RASAwlNnMmcUFkfgxU1BPtyxpTOIaIQffkOjjqzjPHg,988
16
+ gnpyapi/core/route/status_route.py,sha256=2gVoBXpo-16lmCvcnpq-S0WysNaUKFrrbn-bZbxnoRw,207
17
+ gnpyapi/core/service/__init__.py,sha256=Hc0g8HMsO3WRghOQdRyaLC9zjc2pRrVAB24uYcIfmH0,16
18
+ gnpyapi/core/service/config_service.py,sha256=t3SJ3KRmiJb_PWOZuuzm4pb1tvCZb6a8Pd9HwREC868,74
19
+ gnpyapi/core/service/equipment_service.py,sha256=7auoi_WdPM9YU_Z9f1sil_ECzrU96kP9N67F2F-DeEY,77
20
+ gnpyapi/core/service/path_request_service.py,sha256=h4J9KukBE1mgrfDFxJYtzo1_WS5NVt2zGE6fWGNlQNc,1315
21
+ gnpyapi/core/service/topology_service.py,sha256=RbY3sH4XAAcK_Gok35-uAgQ5DoavKuKR6DNdib8ThQE,76
22
+ gnpyapi/exampledata/planning_demand_example.json,sha256=3RMJyvx9MaEnmDI6mE3LrCLze0PZ02PFGsLtQuonl0k,52086
23
+ gnpyapi/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ gnpyapi/yang/gnpy-api@2021-01-06.yang,sha256=zBRxQnn9mL2mA8W-su_Om2LGJtCiuZZ83qUNR6XR_Do,1817
25
+ gnpyapi/yang/gnpy-eqpt-config@2020-10-22.yang,sha256=l39oJ6R_lxFxYM8t9-Ydvzv4JTDjQDjj5PNR2wGMCu4,9582
26
+ gnpyapi/yang/gnpy-eqpt-config@2025-01-20.yang,sha256=ZoZiegd-2S7HSgWx8HUAg_0r56C37PrBthw8Q1Hg_2g,18954
27
+ gnpyapi/yang/gnpy-network-topology@2020-10-22.yang,sha256=6AMEsA_tAU8v_-ZpFD7DtESoQ5H2Mpx3ImdEkg91lh8,5901
28
+ gnpyapi/yang/gnpy-network-topology@2025-01-20.yang,sha256=Zgk1duD426buLNd2qCFYRhuJgyE9NfbQRTltG-gBMa8,13312
29
+ gnpyapi/yang/gnpy-path-computation-simplified@2020-10-22.yang,sha256=DNrK_EUSW61kVsj-DhpM_XJgpbKtc4_LEtElIEyAR-E,14435
30
+ gnpyapi/yang/gnpy-path-computation@2025-01-21.yang,sha256=ayLYz7DC3exogGWqSEv2aoUoaxdmfigXi9Vug1iKKcA,15148
31
+ gnpyapi/yang/ietf-layer0-types@2024-03-04.yang,sha256=Ls7obP0ltyBqnzdKXbbXOYo6d-2Ye3myQD0WDwTl8a4,66052
32
+ gnpyapi/yang/ietf-optical-impairment-topology@2024-05-21.yang,sha256=v3wE9tGcexQmdxRiAq4VPNL1XJuub5lNWHEj5lFXtHk,62035
33
+ gnpyapi/yang/ietf-routing-types@2017-12-04.yang,sha256=v6PUizT3btVEAGpMXYdzYg4WRXSyT2OVre8Qwuy2z74,24480
34
+ gnpyapi/yang/ietf-te-topology@2020-08-06.yang,sha256=f4nGBPahXT8JM_erFyChf92uejqmRrD0L4qUT_uZWTw,58841
35
+ gnpyapi/yang/ietf-te-types@2022-10-21.yang,sha256=E8vBrKPGvsQruOPRhD6nEbEsDB39Ktz3DWVDAz9WEIs,104223
36
+ gnpy_api-0.0.0.dist-info/METADATA,sha256=Rr9w9IRGn8yyPlNSF3lyMVGBdTkdS8oosLpyR3W4rt0,2935
37
+ gnpy_api-0.0.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
38
+ gnpy_api-0.0.0.dist-info/pbr.json,sha256=SbHTygR5eU601azgsndj9fyL6GpBxx3ja7mfWMIT3g8,47
39
+ gnpy_api-0.0.0.dist-info/top_level.txt,sha256=Yq6ziGLLjNZbDSPguICvEMWftNr5FIGJz7GiiD9PhXE,8
40
+ gnpy_api-0.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (78.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ AndreaDAmico <adamico@nec-labs.com>
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025, Telecom Infra Project
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1 @@
1
+ {"git_version": "f9f7ba5", "is_release": false}
@@ -0,0 +1 @@
1
+ gnpyapi
gnpyapi/__init__.py ADDED
File without changes
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env python
2
+
3
+ """GNPy official API
4
+ """
5
+ from flask import Flask
6
+
7
+ API_VERSION = "/api/v0.1"
8
+
9
+ app = Flask(__name__)
10
+
11
+ import gnpyapi.core.route.path_request_route # noqa: E402
12
+ import gnpyapi.core.route.status_route # noqa: F401, E402
@@ -0,0 +1 @@
1
+ # coding: utf-8
@@ -0,0 +1,14 @@
1
+ # coding: utf-8
2
+
3
+
4
+ class ConfigError(Exception):
5
+ """ Exception raise for configuration file error
6
+ Attributes:
7
+ message -- explanation of the error
8
+ """
9
+
10
+ def __init__(self, message):
11
+ self.message = message
12
+
13
+ def __str__(self):
14
+ return self.message
@@ -0,0 +1,14 @@
1
+ # coding: utf-8
2
+
3
+
4
+ class EquipmentError(Exception):
5
+ """ Exception raise for equipment error
6
+ Attributes:
7
+ message -- explanation of the error
8
+ """
9
+
10
+ def __init__(self, message):
11
+ self.message = message
12
+
13
+ def __str__(self):
14
+ return self.message
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ import json
3
+ import re
4
+
5
+ import werkzeug
6
+
7
+ from gnpyapi.core.model.error import Error
8
+
9
+ _reaesc = re.compile(r'\x1b[^m]*m')
10
+
11
+
12
+ def common_error_handler(exception):
13
+ """
14
+
15
+ :type exception: Exception
16
+
17
+ """
18
+ status_code = 500
19
+ if not isinstance(exception, werkzeug.exceptions.HTTPException):
20
+ exception = werkzeug.exceptions.InternalServerError()
21
+ exception.description = "Something went wrong on our side."
22
+ else:
23
+ status_code = exception.code
24
+ response = Error(message=exception.name, description=exception.description,
25
+ code=status_code)
26
+
27
+ return werkzeug.Response(response=json.dumps(response.__dict__), status=status_code, mimetype='application/json')
28
+
29
+
30
+ def bad_request_handler(exception):
31
+ exception_str = " ".join(str(exception).split())
32
+ response = Error(message='bad request', description=_reaesc.sub('', exception_str.replace("\n", " ")),
33
+ code=400)
34
+ return werkzeug.Response(response=json.dumps(response.__dict__), status=400, mimetype='application/json')
@@ -0,0 +1,14 @@
1
+ # coding: utf-8
2
+
3
+
4
+ class PathComputationError(Exception):
5
+ """ Exception raise for path computation error error
6
+ Attributes:
7
+ message -- explanation of the error
8
+ """
9
+
10
+ def __init__(self, message):
11
+ self.message = message
12
+
13
+ def __str__(self):
14
+ return self.message
@@ -0,0 +1,13 @@
1
+ # coding: utf-8
2
+
3
+ class TopologyError(Exception):
4
+ """ Exception raise for topology error
5
+ Attributes:
6
+ message -- explanation of the error
7
+ """
8
+
9
+ def __init__(self, message):
10
+ self.message = message
11
+
12
+ def __str__(self):
13
+ return self.message
@@ -0,0 +1 @@
1
+ # coding: utf-8
@@ -0,0 +1,17 @@
1
+ # coding: utf-8
2
+
3
+
4
+ class Error:
5
+
6
+ def __init__(self, code: int = None, message: str = None, description: str = None):
7
+ """Error
8
+ :param code: The code of this Error.
9
+ :type code: int
10
+ :param message: The message of this Error.
11
+ :type message: str
12
+ :param description: The description of this Error.
13
+ :type description: str
14
+ """
15
+ self.code = code
16
+ self.message = message
17
+ self.description = description
@@ -0,0 +1,8 @@
1
+ # coding: utf-8
2
+
3
+
4
+ class Result:
5
+
6
+ def __init__(self, message: str = None, description: str = None):
7
+ self.message = message
8
+ self.description = description
@@ -0,0 +1 @@
1
+ # coding: utf-8
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+
3
+ from flask import request
4
+
5
+ from gnpyapi.core import app
6
+ from gnpyapi.core.exception.equipment_error import EquipmentError
7
+ from gnpyapi.core.exception.topology_error import TopologyError
8
+ from gnpyapi.core.service.path_request_service import PathRequestService
9
+ from gnpyapi.core import API_VERSION
10
+
11
+ PATH_REQUEST_BASE_PATH = '/path-request'
12
+
13
+
14
+ @app.route(API_VERSION + PATH_REQUEST_BASE_PATH, methods=['POST'])
15
+ @app.route(PATH_REQUEST_BASE_PATH, methods=['POST'])
16
+ def path_request(path_request_service: PathRequestService):
17
+ data = request.json
18
+ service = data['gnpy-api:service']
19
+ if 'gnpy-api:topology' in data:
20
+ topology = data['gnpy-api:topology']
21
+ else:
22
+ raise TopologyError('No topology found in request')
23
+ if 'gnpy-api:equipment' in data:
24
+ equipment = data['gnpy-api:equipment']
25
+ else:
26
+ raise EquipmentError('No equipment found in request')
27
+
28
+ return path_request_service.path_request(topology, equipment, service), 201
@@ -0,0 +1,8 @@
1
+ # coding: utf-8
2
+ from gnpyapi.core import app
3
+ from gnpyapi.core import API_VERSION
4
+
5
+
6
+ @app.route(API_VERSION + '/status', methods=['GET'])
7
+ def api_status():
8
+ return {"version": "v0.1", "status": "ok"}, 200
@@ -0,0 +1 @@
1
+ # coding: utf-8
@@ -0,0 +1,4 @@
1
+ # coding: utf-8
2
+ class ConfigService:
3
+ def __init__(self):
4
+ pass
@@ -0,0 +1,5 @@
1
+ # coding: utf-
2
+ class EquipmentService:
3
+
4
+ def __init__(self):
5
+ pass
@@ -0,0 +1,36 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import logging
4
+
5
+ from gnpy.core.exceptions import EquipmentConfigError, NetworkTopologyError
6
+ from gnpy.tools.json_io import results_to_json, load_eqpt_topo_from_json
7
+ from gnpy.tools.worker_utils import designed_network, planning
8
+ from gnpyapi.core.exception.topology_error import TopologyError
9
+
10
+ from gnpyapi.core.exception.equipment_error import EquipmentError
11
+
12
+ _logger = logging.getLogger(__name__)
13
+
14
+
15
+ class PathRequestService:
16
+
17
+ def __init__(self):
18
+ pass
19
+
20
+ @staticmethod
21
+ def path_request(topology: dict, equipment: dict, service: dict = None) -> dict:
22
+ try:
23
+ (equipment, network) = load_eqpt_topo_from_json(equipment, topology)
24
+ network, _, _ = designed_network(equipment, network)
25
+ # todo parse request
26
+ _, _, _, _, _, result = planning(network, equipment, service)
27
+ return results_to_json(result)
28
+ except EquipmentConfigError as e:
29
+ _logger.error(f"An equipment error occurred: {e}")
30
+ raise EquipmentError(str(e))
31
+ except NetworkTopologyError as e:
32
+ _logger.error(f"An equipment error occurred: {e}")
33
+ raise TopologyError(str(e))
34
+ except Exception as e:
35
+ _logger.error(f"An error occurred during path request: {e}")
36
+ raise
@@ -0,0 +1,5 @@
1
+ # coding: utf-
2
+
3
+ class TopologyService:
4
+ def __init__(self):
5
+ pass