holado 0.4.1__py3-none-any.whl → 0.4.2__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.

Potentially problematic release.


This version of holado might be problematic. Click here for more details.

Files changed (42) hide show
  1. holado/common/handlers/object.py +6 -0
  2. {holado-0.4.1.dist-info → holado-0.4.2.dist-info}/METADATA +2 -1
  3. {holado-0.4.1.dist-info → holado-0.4.2.dist-info}/RECORD +42 -21
  4. holado_core/tools/abstracts/blocking_command_service.py +9 -1
  5. holado_django/__init__.py +31 -0
  6. holado_django/server/HOWTO.txt +27 -0
  7. holado_django/server/django_projects/rest_api/db.sqlite3 +0 -0
  8. holado_django/server/django_projects/rest_api/manage.py +22 -0
  9. holado_django/server/django_projects/rest_api/rest_api/__init__.py +0 -0
  10. holado_django/server/django_projects/rest_api/rest_api/application/__init__.py +0 -0
  11. holado_django/server/django_projects/rest_api/rest_api/application/admin.py +3 -0
  12. holado_django/server/django_projects/rest_api/rest_api/application/apps.py +9 -0
  13. holado_django/server/django_projects/rest_api/rest_api/application/migrations/__init__.py +0 -0
  14. holado_django/server/django_projects/rest_api/rest_api/application/models.py +3 -0
  15. holado_django/server/django_projects/rest_api/rest_api/application/tests.py +3 -0
  16. holado_django/server/django_projects/rest_api/rest_api/application/views.py +6 -0
  17. holado_django/server/django_projects/rest_api/rest_api/asgi.py +16 -0
  18. holado_django/server/django_projects/rest_api/rest_api/settings.py +130 -0
  19. holado_django/server/django_projects/rest_api/rest_api/urls.py +35 -0
  20. holado_django/server/django_projects/rest_api/rest_api/wsgi.py +16 -0
  21. holado_django/server/django_server.py +110 -0
  22. holado_django/server/grpc_django_server.py +57 -0
  23. holado_django/tests/behave/steps/__init__.py +16 -0
  24. holado_django/tests/behave/steps/django_server_steps.py +83 -0
  25. holado_grpc/tests/behave/steps/private/api/grpc_steps.py +9 -9
  26. holado_multitask/multitasking/multitask_manager.py +36 -10
  27. holado_python/standard_library/ssl/resources/certificates/tcpbin.crt +16 -16
  28. holado_python/standard_library/ssl/resources/certificates/tcpbin.key +26 -26
  29. holado_rest/api/rest/TODO.txt +1 -1
  30. holado_rest/tests/behave/steps/private/api/rest_steps.py +11 -13
  31. holado_system/system/command/command.py +31 -9
  32. holado_system/system/global_system.py +3 -3
  33. test_holado/environment.py +6 -4
  34. test_holado/features/NonReg/api/REST.feature +7 -2
  35. test_holado/features/NonReg/api/gRPC.feature +0 -6
  36. test_holado/initialize_holado.py +62 -0
  37. test_holado/logging.conf +3 -0
  38. test_holado/tools/django/api_rest/db.sqlite3 +0 -0
  39. {holado-0.4.1.dist-info → holado-0.4.2.dist-info}/WHEEL +0 -0
  40. {holado-0.4.1.dist-info → holado-0.4.2.dist-info}/licenses/LICENSE +0 -0
  41. /holado_helper/holado_module_template/{test → tests}/behave/steps/__init__.py +0 -0
  42. /holado_helper/holado_module_template/{test → tests}/behave/steps/private/__init__.py +0 -0
@@ -0,0 +1,110 @@
1
+
2
+ #################################################
3
+ # HolAdo (Holistic Automation do)
4
+ #
5
+ # (C) Copyright 2021-2025 by Eric Klumpp
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
+ #
9
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10
+
11
+ # The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the Software.
12
+ #################################################
13
+
14
+ from holado_core.common.exceptions.technical_exception import TechnicalException
15
+ import logging
16
+ from holado_core.common.tools.tools import Tools
17
+ import os
18
+ from holado_core.tools.abstracts.blocking_command_service import BlockingCommandService
19
+ from holado_system.system.command.command import CommandStates
20
+ from holado_core.common.handlers.wait import WaitFuncResultVerifying
21
+ from holado.common.handlers.object import DeleteableObject
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ try:
26
+ import django # @UnusedImport
27
+ with_django = True
28
+ except Exception as exc:
29
+ if Tools.do_log(logger, logging.DEBUG):
30
+ logger.debug(f"DjangoServer is not available. Initialization failed on error: {exc}")
31
+ with_django = False
32
+
33
+
34
+ class DjangoServer(DeleteableObject):
35
+ """
36
+ Django server
37
+ """
38
+
39
+ @classmethod
40
+ def is_available(cls):
41
+ return with_django
42
+
43
+ def __init__(self, name, django_project_path, port=8000, runserver_args=None):
44
+ super().__init__(name)
45
+
46
+ self.__django_project_path = django_project_path
47
+ self.__runserver_port = port
48
+ self.__runserver_args = runserver_args
49
+ self.__project_command = None
50
+
51
+ if not os.path.exists(django_project_path):
52
+ raise TechnicalException(f"Django project doesn't exist (project path: '{django_project_path}')")
53
+
54
+ def _delete_object(self):
55
+ if self.__project_command is not None and self.__project_command.status == CommandStates.Running:
56
+ self.stop()
57
+
58
+ @property
59
+ def django_project_path(self):
60
+ return self.__django_project_path
61
+
62
+ @property
63
+ def runserver_port(self):
64
+ return self.__runserver_port
65
+
66
+ @property
67
+ def runserver_args(self):
68
+ return self.__runserver_args
69
+
70
+ def start(self):
71
+ self.__start_server_by_command()
72
+
73
+ def __start_server_by_command(self):
74
+ self.__project_command = self._new_project_command()
75
+ self.__project_command.start()
76
+ self._wait_until_server_is_reachable()
77
+
78
+ def _new_project_command(self):
79
+ manage_path = os.path.join(self.django_project_path, "manage.py")
80
+ cmd = f"python {manage_path} runserver {self.runserver_port}"
81
+ if self.runserver_args:
82
+ cmd += f" {self.runserver_args}"
83
+
84
+ res = BlockingCommandService(f"Command running Django server '{self.name}'", cmd)
85
+ res.auto_stop = True
86
+
87
+ return res
88
+
89
+ def _wait_until_server_is_reachable(self):
90
+ import requests
91
+ url = f"http://127.0.0.1:{self.runserver_port}"
92
+ redo = WaitFuncResultVerifying("server is reachable",
93
+ lambda: requests.get(url),
94
+ lambda result: result and result.status_code == 200 )
95
+ redo.ignoring(Exception)
96
+ redo.polling_every(0.01)
97
+ redo.execute()
98
+
99
+ def stop(self):
100
+ if self.__project_command is None or self.__project_command.status != CommandStates.Running:
101
+ raise TechnicalException(f"Django server of project '{self.name}' is not running (status: {self.__project_command.status if self.__project_command else 'Unkown'})")
102
+ self.__project_command.stop()
103
+
104
+ def join(self, timeout=None):
105
+ self.__project_command.join(timeout)
106
+
107
+
108
+
109
+
110
+
@@ -0,0 +1,57 @@
1
+
2
+ #################################################
3
+ # HolAdo (Holistic Automation do)
4
+ #
5
+ # (C) Copyright 2021-2025 by Eric Klumpp
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
+ #
9
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10
+
11
+ # The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the Software.
12
+ #################################################
13
+
14
+ import logging
15
+ import os
16
+ from holado_core.tools.abstracts.blocking_command_service import BlockingCommandService
17
+ from holado_django.server.django_server import DjangoServer
18
+ from holado_core.common.handlers.wait import WaitFuncResultVerifying
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class GrpcDjangoServer(DjangoServer):
24
+ """
25
+ gRPC Django server
26
+ """
27
+
28
+ def __init__(self, name, django_project_path, runserver_args=None):
29
+ super().__init__(name, django_project_path, port=50051, runserver_args=runserver_args)
30
+
31
+ def _new_project_command(self):
32
+ manage_path = os.path.join(self.django_project_path, "manage.py")
33
+ cmd = f"python {manage_path} grpcrunserver"
34
+ if self.runserver_args:
35
+ cmd += f" {self.runserver_args}"
36
+
37
+ res = BlockingCommandService(f"Command running gRPC Django server '{self.name}'", cmd)
38
+ res.auto_stop = True
39
+
40
+ return res
41
+
42
+ def _wait_until_server_is_reachable(self):
43
+ import grpc_requests
44
+
45
+ endpoint = f"localhost:{self.runserver_port}"
46
+ def request_is_unimplemented():
47
+ try:
48
+ grpc_requests.Client.get_by_endpoint(endpoint)
49
+ except Exception as exc:
50
+ if "status = StatusCode.UNIMPLEMENTED" in str(exc):
51
+ return True
52
+ return False
53
+ redo = WaitFuncResultVerifying("server is reachable", request_is_unimplemented, lambda result: result )
54
+ redo.polling_every(0.01)
55
+ redo.execute()
56
+
57
+
@@ -0,0 +1,16 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ #################################################
4
+ # HolAdo (Holistic Automation do)
5
+ #
6
+ # (C) Copyright 2021-2025 by Eric Klumpp
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11
+
12
+ # The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the Software.
13
+ #################################################
14
+
15
+
16
+ from .django_server_steps import *
@@ -0,0 +1,83 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ #################################################
4
+ # HolAdo (Holistic Automation do)
5
+ #
6
+ # (C) Copyright 2021-2025 by Eric Klumpp
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11
+
12
+ # The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the Software.
13
+ #################################################
14
+
15
+ from holado.common.context.session_context import SessionContext
16
+ from holado_test.behave.behave import * # @UnusedWildImport
17
+ import logging
18
+ from holado_test.scenario.step_tools import StepTools
19
+ from holado_django.server.django_server import DjangoServer
20
+ from holado_test.behave.scenario.behave_step_tools import BehaveStepTools
21
+ from holado_value.common.tables.converters.value_table_converter import ValueTableConverter
22
+ from holado_core.common.exceptions.technical_exception import TechnicalException
23
+ from holado_django.server.grpc_django_server import GrpcDjangoServer
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ def __get_session_context():
29
+ return SessionContext.instance()
30
+
31
+ def __get_scenario_context():
32
+ return __get_session_context().get_scenario_context()
33
+
34
+ def __get_text_interpreter():
35
+ return __get_scenario_context().get_text_interpreter()
36
+
37
+ def __get_variable_manager():
38
+ return __get_scenario_context().get_variable_manager()
39
+
40
+
41
+
42
+ @Given(r"(?P<var_name>{Variable}) = new Django server")
43
+ def step_impl(context, var_name):
44
+ var_name = StepTools.evaluate_variable_name(var_name)
45
+ table = BehaveStepTools.get_step_table(context)
46
+
47
+ kwargs = ValueTableConverter.convert_name_value_table_2_json_object(table)
48
+ if 'django_project_path' not in kwargs:
49
+ raise TechnicalException("Parameter 'django_project_path' is required")
50
+ if 'name' not in kwargs:
51
+ kwargs['name'] = f"Django server ({kwargs['django_project_path']})"
52
+
53
+ server = DjangoServer(**kwargs)
54
+
55
+ __get_variable_manager().register_variable(var_name, server)
56
+
57
+ @Given(r"(?P<var_name>{Variable}) = new gRPC Django server")
58
+ def step_impl(context, var_name):
59
+ var_name = StepTools.evaluate_variable_name(var_name)
60
+ table = BehaveStepTools.get_step_table(context)
61
+
62
+ kwargs = ValueTableConverter.convert_name_value_table_2_json_object(table)
63
+ if 'django_project_path' not in kwargs:
64
+ raise TechnicalException("Parameter 'django_project_path' is required")
65
+ if 'name' not in kwargs:
66
+ kwargs['name'] = f"Django server ({kwargs['django_project_path']})"
67
+
68
+ server = GrpcDjangoServer(**kwargs)
69
+
70
+ __get_variable_manager().register_variable(var_name, server)
71
+
72
+
73
+ @Step(r"start \(Django server: (?P<var_server>{Variable})\)")
74
+ def step_impl(context, var_server):
75
+ server = StepTools.evaluate_variable_value(var_server)
76
+ server.start()
77
+
78
+ @Step(r"stop \(Django server: (?P<var_server>{Variable})\)")
79
+ def step_impl(context, var_server):
80
+ server = StepTools.evaluate_variable_value(var_server)
81
+ server.stop()
82
+
83
+
@@ -49,18 +49,18 @@ def step_impl(context, var_name):
49
49
  var_name = StepTools.evaluate_variable_name(var_name)
50
50
 
51
51
  here = os.path.abspath(os.path.dirname(__file__))
52
- api_grpc_path = os.path.normpath(os.path.join(here, "..", "..", "..", "..", "..", "..", "..", "tests", "behave", "test_holado", "tools", "django", "api_grpc"))
53
- manage_path = os.path.join(api_grpc_path, "manage.py")
54
- cmd = f"python {manage_path} grpcrunserver"
52
+ django_project_path = os.path.normpath(os.path.join(here, "..", "..", "..", "..", "..", "..", "..", "tests", "behave", "test_holado", "tools", "django", "api_grpc"))
55
53
 
56
- obj = BlockingCommandService("internal gRPC server", cmd)
57
- obj.auto_stop = True
58
- obj.start()
59
-
60
- __get_variable_manager().register_variable(var_name, obj)
54
+ execute_steps(u"""
55
+ Given {var_name} = new gRPC Django server
56
+ | Name | Value |
57
+ | 'name' | 'gRPC server for holado tests' |
58
+ | 'django_project_path' | '{django_project_path}' |
59
+ When start (Django server: {var_name})
60
+ """.format(var_name=var_name, django_project_path=django_project_path))
61
61
 
62
62
  # Update imported grpc messages and services
63
- proto_path = os.path.join(api_grpc_path, "api_grpc", "api1", "proto")
63
+ proto_path = os.path.join(django_project_path, "api_grpc", "api1", "proto")
64
64
  __get_session_context().protobuf_messages.import_all_compiled_proto(proto_path)
65
65
  __get_session_context().grpc_services.import_all_compiled_proto(proto_path)
66
66
 
@@ -22,6 +22,8 @@ from holado_core.common.tools.tools import Tools
22
22
  from holado_python.standard_library.typing import Typing
23
23
  from holado.common.handlers.undefined import default_context
24
24
  from holado.common.context.session_context import SessionContext
25
+ import psutil
26
+ import signal
25
27
 
26
28
  logger = logging.getLogger(__name__)
27
29
 
@@ -60,16 +62,40 @@ class MultitaskManager(object):
60
62
  if pid is None:
61
63
  return os.getppid()
62
64
  else:
63
- if GlobalSystem.get_os_type() == OSTypes.Linux:
64
- # Read /proc/<pid>/status and look for the line `PPid:\t120517\n`
65
- with open(f"/proc/{pid}/status", encoding="ascii") as f:
66
- for line in f.readlines():
67
- if line.startswith("PPid:\t"):
68
- return int(line[6:])
69
- raise TechnicalException(f"No PPid line found in /proc/{pid}/status")
70
- else:
71
- raise TechnicalException(f"Unmanaged OS type {GlobalSystem.get_os_type().name}")
72
-
65
+ process = psutil.Process(pid)
66
+ return process.ppid()
67
+
68
+ @classmethod
69
+ def get_children_process_ids(cls, pid=None, recursive=False):
70
+ if pid is None:
71
+ pid = os.getpid()
72
+
73
+ process = psutil.Process(pid)
74
+ children = process.children(recursive=recursive)
75
+ return [c.pid for c in children]
76
+
77
+ @classmethod
78
+ def kill_process(cls, pid, sig=signal.SIGTERM, do_kill_children=True, recursively=True):
79
+ try:
80
+ process = psutil.Process(pid)
81
+ except psutil.NoSuchProcess:
82
+ return
83
+
84
+ # Kill children
85
+ if do_kill_children:
86
+ children = process.children(recursive=recursively)
87
+ for proc in children:
88
+ try:
89
+ proc.send_signal(sig)
90
+ except signal.SIGTERM:
91
+ pass
92
+
93
+ # Kill process
94
+ try:
95
+ process.send_signal(sig)
96
+ except signal.SIGTERM:
97
+ pass
98
+
73
99
  @classmethod
74
100
  def has_thread_native_id(cls):
75
101
  from holado_multitask.multithreading.threadsmanager import ThreadsManager
@@ -2,20 +2,20 @@
2
2
  MIIDZTCCAk2gAwIBAgIBKjANBgkqhkiG9w0BAQsFADCBizELMAkGA1UEBhMCVVMx
3
3
  CzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQKDAZ0
4
4
  Y3BiaW4xDDAKBgNVBAsMA29wczETMBEGA1UEAwwKdGNwYmluLmNvbTEjMCEGCSqG
5
- SIb3DQEJARYUaGFycnliYWdkaUBnbWFpbC5jb20wHhcNMjUwNjI2MTYxMzE3WhcN
6
- MjUwNjI3MTYxMzE3WjAcMRowGAYDVQQDDBF0Y3BiaW4uY29tLWNsaWVudDCCASIw
7
- DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO4WKCcsF4eCjLUeUT3/3IbM8m0N
8
- Z5Fgmqe3K9lDOXFQa/dX98/gGPswQiWkJ3ALspYgDGnfqEQEc5kBrTNpom0xoWPD
9
- vgv7M4O222bMtVvXyvE5/Absw/VYGhW5KGln2k/4hEl0Xl2kS7P0KMlrczrjDyqW
10
- NhGMvgWfSSF1D1fP1tx4E/74TnCrUQy332wlprxB567kIlPLUe4zbNT1PvAhXXj1
11
- r9kKYBy6ontb+VWnrYraD3YhLOsez3OocY32JfvB8jFgjaxKTFMCchOJaFuFZEl8
12
- /wCPawwCdG8aIvC+EVuttbA3F/s3vDjQRTKW9WJ1KoJhRIwYbwwmvNIycMkCAwEA
13
- AaNCMEAwHQYDVR0OBBYEFMXW798gAD4iFP15opS+N49NX/9HMB8GA1UdIwQYMBaA
14
- FOLuMowBSAZfV5v82LmlaIIOvU/DMA0GCSqGSIb3DQEBCwUAA4IBAQAtxMjai8tc
15
- VEA0TTPeDplTyDUYZqwnb+wbxOmb8+CTwt7hj/o1vDS82QGoKINiHyRGhyJrfiTQ
16
- 6TqBRxZMt52IdYwQoAMDnxyjxLdeVr0RNbxGziWBs3fjjahaEX/BMqzu5wd4mcpi
17
- UUmauULmYtY3vKiw8wsUEEkzzMM3fNJklMdMcR47cbH783FuJ7clhYaE/pJlq8RD
18
- wNIgume1u3BstRpIHafuZOy4Rg2nDiIuQoIORq7n8eRfgU9ZxVL7kZOgPFiTuokq
19
- 0EgnEhwOTUahO6aFpN9j/wCIANLrf1bZWw7v80bno7aFQ1XOm2RW5yJywkc7LFqi
20
- Dia/obmoNitb
5
+ SIb3DQEJARYUaGFycnliYWdkaUBnbWFpbC5jb20wHhcNMjUwNzAxMTQyMDI3WhcN
6
+ MjUwNzAyMTQyMDI3WjAcMRowGAYDVQQDDBF0Y3BiaW4uY29tLWNsaWVudDCCASIw
7
+ DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALmLkz54fCoSSM/uG945GvhavQF8
8
+ unsEDd0RuHWZPIBcrlEzhLLiiiegET4UFlywF4wSfbiNGgwr3Uw+6ph3xLrNs91B
9
+ EYqOGesT9wRPMlon88qsaVIFfbqfg4e1m0si+/NwZMk+5bBeYC0blXk7HiTZ5KJt
10
+ QNLeduIYaPSuyq+qxl/xDqWOifbKcgycitgCPKPxohtwa5h6IjU3WYb7+xNPt3cx
11
+ +hoCwfab1NgVBse6ZsUf81nkCEVKKLaNvlDkmXp9ItqZZay+0/yzLCgNyf8gDprU
12
+ 8u+nDM+pcz83OtIQWVeN5MtFjkA4nik55yqdlqnB1OWzEzaMSMh6Vsy13xMCAwEA
13
+ AaNCMEAwHQYDVR0OBBYEFG5H5JaQhWehs8VRJu88HbHgK9CvMB8GA1UdIwQYMBaA
14
+ FOLuMowBSAZfV5v82LmlaIIOvU/DMA0GCSqGSIb3DQEBCwUAA4IBAQCs93UG22r0
15
+ ZIF10IAoXVbPWOeXpKJgWpsQYURsGw7sB+Cgr/vK3P1lkZVMl72LUzNazmOQldev
16
+ zUQ0TtOq24go1iAYWc7zYlxo9OHNkbAUDoViKP63K/CkXNCIkXg00vmEUco1PQWU
17
+ +OEaTNrQCeiW2eqYie9WNc4T5a7aUHT3Xno7G8KXTM7gtW35FNI/b0CwVWHRFzev
18
+ n9VhRI9xrNYxpDqqxYu7diIV4TQVmscmNicuEtDB98kJ7yOAhlpzQhIFi1vQEefD
19
+ 4gZZ3ihlLd2XJWxiXZgeiz7dzva4oGcr9mU5Uohza3u/sjF34kx02+gNuv8x4Wil
20
+ CU9Rl0WFGJ00
21
21
  -----END CERTIFICATE-----
@@ -1,28 +1,28 @@
1
1
  -----BEGIN PRIVATE KEY-----
2
- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDuFignLBeHgoy1
3
- HlE9/9yGzPJtDWeRYJqntyvZQzlxUGv3V/fP4Bj7MEIlpCdwC7KWIAxp36hEBHOZ
4
- Aa0zaaJtMaFjw74L+zODtttmzLVb18rxOfwG7MP1WBoVuShpZ9pP+IRJdF5dpEuz
5
- 9CjJa3M64w8qljYRjL4Fn0khdQ9Xz9bceBP++E5wq1EMt99sJaa8Qeeu5CJTy1Hu
6
- M2zU9T7wIV149a/ZCmAcuqJ7W/lVp62K2g92ISzrHs9zqHGN9iX7wfIxYI2sSkxT
7
- AnITiWhbhWRJfP8Aj2sMAnRvGiLwvhFbrbWwNxf7N7w40EUylvVidSqCYUSMGG8M
8
- JrzSMnDJAgMBAAECggEAB6aan7ehAe/GCcPxpGZmdcZ8O9jkbR2HmsAeHps0a1nE
9
- PmyLQBjZdKj2VzNROR6HHJ/nBjfztkvWUyJkqHRKjeu8XfJaVxQ5cZAcuM73PP5Q
10
- PTQ5zxcorOep3uzWSZzWvR8lUR72MVgoPp4n+WEEkO3e/OVa03GMYa3esb6LQa0S
11
- QcPkMwsxgPZQ1V9s85IIyVBn2ebbOhDbU1mz0FhzWtJ18Jikm0KhJuHUwwXAwO4H
12
- aINMbSkckOtyJwQ2Iu2Me8/ZiFLUTCMkdym2fb3jkWS+uiHxJye0Pg4AnXtIZE+W
13
- cUwwrZE+TnhodiqoAc3CgD0ZkqIpp07KMl7FM9jGaQKBgQD7vQHHBHKLFbW6e9dT
14
- ruZR+4kwOeTA1jzKlr2D8/Y6XJAPVG2UCn1oxfMTp6NkqC9nmalct4FH6lzpleZG
15
- Duwm2kxhmI+kxPj5MXH/MDlMSL3nw06mQD8tWUM9u6M73Rm6DMg5VMH3WqcUzycl
16
- 4FZDl0LcABDZsP+3f2A34l30uwKBgQDyHfxC5bA93cbvVzo3Qd5I611oKgg9J3vh
17
- /5I2bT3HKCIb7ruAthbDIO5/5K0ufIIzKAxcI9EJd2ViSO2MHhMQOfkrcKh6Kzb+
18
- u61r52ZRKN1aI2UiZzqtUTpkOetevRh1WMmH0CmItxZ/OpqDq/2OyQ6XedZWvd5t
19
- /PockMZaSwKBgBtZXWgRw5/4q9Wmvq4Iwl0FxtHGeGO64r/fwJclWgrdI4mG0qDS
20
- wu0vFEl+XPICk6Pdvdik1xbJD28RKgNSe7V84e94c6KjA6mPBaODybXP8VHMli7Z
21
- rANxPyzlxcYrLzBXUylnW+tTnfNUzhv/U1/kfw8HsszzvdRhskJgBXUhAoGBAIHU
22
- sHwS/PH/5fhcFvyglpkVlS4RM//PF3A2Auqqo7ETBU9jMiqv+f7CvwHX10IRRCQ4
23
- eoBCOIrR+oy8vJ0hV2mhHMs9iyqP19q7OyCcolItDp2SU14iauWbpCswn9Vaoy3x
24
- 4YexiUQloaux+j9XUA1sJSX3EIfNIuRp/piozaSHAoGAWODO4Ug0aU3SxHoj3muQ
25
- dgQmAcwNKif7UOWCcB/6qQFDMqDqiWuqPX1x7sUYjN61Dd2AwoOanI8phDsp2R4/
26
- 7ywBy2es5ePrk5Ma332QA/6rxkLVXGaGEV4mCVziF4PEGsbMZzx7YDIZoXUwuKw0
27
- JLVTXQhAmlEnNoMi1T/zLB8=
2
+ MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC5i5M+eHwqEkjP
3
+ 7hveORr4Wr0BfLp7BA3dEbh1mTyAXK5RM4Sy4oonoBE+FBZcsBeMEn24jRoMK91M
4
+ PuqYd8S6zbPdQRGKjhnrE/cETzJaJ/PKrGlSBX26n4OHtZtLIvvzcGTJPuWwXmAt
5
+ G5V5Ox4k2eSibUDS3nbiGGj0rsqvqsZf8Q6ljon2ynIMnIrYAjyj8aIbcGuYeiI1
6
+ N1mG+/sTT7d3MfoaAsH2m9TYFQbHumbFH/NZ5AhFSii2jb5Q5Jl6fSLamWWsvtP8
7
+ sywoDcn/IA6a1PLvpwzPqXM/NzrSEFlXjeTLRY5AOJ4pOecqnZapwdTlsxM2jEjI
8
+ elbMtd8TAgMBAAECggEAfLWcfSOcSObLO76NyppVT1IlsWc1K9O4wbrUYW5iZOBm
9
+ Zbub2GQ9eY6zqCb2NMxCt2oCSFXGiSG+dy3eniX5+5ig6PiAIsGKGB/uKl5UuJYb
10
+ 3UBu9astK49lZ4Sf4Sudbq0/gKge16FHQWpF2BrtEtXFP4rxRAo0m5jOio8lOlYG
11
+ VSBFrkww7aS0pbymELj+Yg0twrUQjeG/hhoA4juiAq8T6oilrNfxhgc8pi2e23Ix
12
+ iokPjVWWAylKr6+oKhgBj/MKoIcEPeyYem1rBCesO2Th5CHy4YwIsZq+456Wo9/z
13
+ efxjnr+1vrsH+WK83nff3wUMruchKbdP1p9sMSkwqQKBgQD0y8b7sK4omV/JRdS3
14
+ sADYETu2wshP22EuUbkDPICe5/ct1LWlTJAI84MkMcg4rOHuLubD32ttRA3SWnK/
15
+ eqXB3gkcstVhipjj5qH5sg3gCIOa5rmePY1Zn5v75BImYzHYBfqQ0Z99czBNQ4un
16
+ MPMxe1IRT+b6aU7EOWjSWG2X/wKBgQDCCZGpv+pFQqxb5YjPQqDn/7M9wE89b22T
17
+ G2weESzoBrnLCUA4M0QEhhgBkcb9grZQ0aCOzjqKTDF/2AnFOhP6IIzEVxkrsC/0
18
+ c150lU8kiIlkymVF7N5vwayPFUeNqaO5NdHOYu0HkEQ8h9FCFurc0OjA/8xRPXkp
19
+ AGar8P/Y7QKBgE+5jjSqdg4C5Y9Hjt/EEoJMGoaLKXHYoO3U78x+B+W45memvwH2
20
+ zXIc3LkM/Yh3xZ0s6TshqHsNjvLTQkvaReG9znnqRFRgLysKEfagZqRwIWxxeEJx
21
+ CXgG42ZGASM/axxP1isUGj1hJnoDZZgt+QZEg5Xfz/n+EgkWKW1YH1lBAoGAMw7L
22
+ ioxae+Egc4oBpvAUYRfStXQOJc9VWPlFSOAiHefvKbMEeAVdZ4dVd8xBPWIQ0VFn
23
+ 20v+8Xc9KzPQ1loC+bVo9R0qHWneJIfbGfhT+/wFk0UCwxSiL2waGQhzbJ5v24OC
24
+ 8rjrQCtBGWBvuuFG6dX6+RYWUGZJpHVbjvD6kb0CgYAnU00V1AZu39ku1iozaHoY
25
+ STedxCDtGZotOISDFzaqIxaJ1JUeWZ/90ZMIVHdE+pP8n2P8MN7rppBbTN6Mxcbq
26
+ uE9BfZJGN5A8PuugoJWBJwkfnhrEMXXSpz5SPQntCkrET7pkAW2Nx1CWRQKaj1pu
27
+ +CKEVUIhv/elJOdMV9mGbA==
28
28
  -----END PRIVATE KEY-----
@@ -1,2 +1,2 @@
1
- Client with library "requests"
2
1
  Client with library "httpx"
2
+
@@ -17,6 +17,7 @@ from holado_test.behave.behave import * # @UnusedWildImport
17
17
  import os.path
18
18
  import logging
19
19
  from holado_test.scenario.step_tools import StepTools
20
+ from holado_django.server.django_server import DjangoServer
20
21
 
21
22
  logger = logging.getLogger(__name__)
22
23
 
@@ -40,22 +41,19 @@ def step_impl(context, var_name):
40
41
  var_name = StepTools.evaluate_variable_name(var_name)
41
42
 
42
43
  here = os.path.abspath(os.path.dirname(__file__))
43
- manage_path = os.path.normpath(os.path.join(here, "..", "..", "tools", "django", "api_rest", "manage.py"))
44
- cmd = f"python {manage_path} runserver"
44
+ django_project_path = os.path.normpath(os.path.join(here, "..", "..", "..", "..", "..", "..", "..", "tests", "behave", "test_holado", "tools", "django", "api_rest"))
45
45
 
46
46
  execute_steps(u"""
47
- Given {var_name} = new command '{cmd}' with
48
- | Name | Value |
49
- | 'auto_stop' | True |
50
- | 'blocking' | True |
51
- | 'name' | 'internal REST server' |
52
- Given run command {var_name}
53
- Then command {var_name} has status Ready
54
- When wait 5 seconds
55
- Then command {var_name} has status Running
56
- """.format(cmd=cmd, var_name=var_name))
57
-
47
+ Given {var_name} = new Django server
48
+ | Name | Value |
49
+ | 'name' | 'REST server for holado tests' |
50
+ | 'django_project_path' | '{django_project_path}' |
51
+ | 'port' | 8000 |
52
+ When start (Django server: {var_name})
53
+ """.format(var_name=var_name, django_project_path=django_project_path))
58
54
 
55
+
56
+
59
57
  @Given(r"(?P<var_name>{Variable}) = new internal REST client")
60
58
  def step_impl(context, var_name):
61
59
  var_name = StepTools.evaluate_variable_name(var_name)
@@ -20,6 +20,9 @@ import time
20
20
  from enum import IntEnum
21
21
  from holado_core.common.exceptions.functional_exception import FunctionalException
22
22
  import copy
23
+ import signal
24
+ from holado_core.common.tools.tools import Tools
25
+ from holado_multitask.multitasking.multitask_manager import MultitaskManager
23
26
 
24
27
  logger = logging.getLogger(__name__)
25
28
 
@@ -31,6 +34,7 @@ class CommandStates(IntEnum):
31
34
  Success = 3
32
35
  Error = 4
33
36
 
37
+
34
38
  class Command(threading.Thread):
35
39
  """
36
40
  Execute a command in a thread.
@@ -58,6 +62,7 @@ class Command(threading.Thread):
58
62
  self.__callback_delay_ms = None
59
63
  self.__external_parameters = {}
60
64
  self.__subprocess_kwargs = subprocess_kwargs
65
+ self.__stop_signal = signal.SIGTERM
61
66
 
62
67
 
63
68
  @property
@@ -94,29 +99,38 @@ class Command(threading.Thread):
94
99
  return self.__process.returncode
95
100
  else:
96
101
  return None
97
-
102
+
98
103
  @property
99
104
  def callback(self):
100
105
  return self.__callback
101
-
106
+
102
107
  @callback.setter
103
108
  def callback(self, callback):
104
109
  """Set callback called when execution end."""
105
110
  self.__callback = callback
106
-
111
+
107
112
  @property
108
113
  def callback_delay_ms(self):
109
114
  return self.__callback_delay_ms
110
-
115
+
111
116
  @callback_delay_ms.setter
112
117
  def callback_delay_ms(self, delay_ms):
113
118
  """Set callback delay in ms."""
114
119
  self.__callback_delay_ms = delay_ms
115
-
120
+
116
121
  @property
117
122
  def external_parameters(self):
118
123
  return self.__external_parameters
119
-
124
+
125
+ @property
126
+ def stop_signal(self):
127
+ return self.__stop_signal
128
+
129
+ @stop_signal.setter
130
+ def stop_signal(self, stop_signal):
131
+ """Set stop signal."""
132
+ self.__stop_signal = stop_signal
133
+
120
134
  def run(self):
121
135
  logger.debug("Call command: {}".format(self.cmd))
122
136
  try:
@@ -139,7 +153,7 @@ class Command(threading.Thread):
139
153
 
140
154
  self.__stdout = ""
141
155
  self.__stderr = ""
142
- while (self.__process.returncode is None):
156
+ while self.__process.returncode is None:
143
157
  # Wait a small time and in same time yield this thread
144
158
  time.sleep(0.001)
145
159
 
@@ -175,7 +189,7 @@ class Command(threading.Thread):
175
189
  logger.debug(f"Command [{self.cmd}] has finished, calling callback...")
176
190
  self.callback(self)
177
191
  elif self.error is not None:
178
- logger.error(f"Command [{self.cmd}] has finished on error : {self.error}")
192
+ logger.error(f"Command [{self.cmd}] has finished on error: {self.error}")
179
193
  elif self.state == CommandStates.Error:
180
194
  logger.error(f"Command [{self.cmd}] has finished on error code {self.return_code} and stderr: {self.stderr}")
181
195
  elif self.state != CommandStates.Success:
@@ -208,13 +222,21 @@ class Command(threading.Thread):
208
222
  raise FunctionalException(msg)
209
223
 
210
224
  def kill(self):
225
+ # Note: kill is equivalent to terminate in subprocess implementation
211
226
  if self.state == CommandStates.Running:
212
227
  self.__process.kill()
213
228
 
214
229
  def terminate(self):
215
230
  if self.state == CommandStates.Running:
216
231
  self.__process.terminate()
217
-
232
+
233
+ def stop(self):
234
+ if self.state == CommandStates.Running:
235
+ if Tools.do_log(logger, logging.DEBUG):
236
+ logger.debug(f"Stopping command [{self.cmd}] with signal {self.__stop_signal} and PID {self.__process.pid}")
237
+
238
+ MultitaskManager.kill_process(self.__process.pid, sig=self.__stop_signal, do_kill_children=True, recursively=True)
239
+
218
240
  def __repr__(self):
219
241
  return pprint.pformat({'cmd' : self.cmd,
220
242
  'is alive' : self.is_alive() })
@@ -14,9 +14,7 @@
14
14
  import logging
15
15
  from holado.common.handlers.enums import AutoNumber
16
16
  import platform
17
- from holado_system.system.command.command import Command, CommandStates
18
17
  from holado_core.common.exceptions.technical_exception import TechnicalException
19
- from holado_system.system.command.command_result import CommandResult
20
18
  import time
21
19
  import os
22
20
  from holado_core.common.tools.tools import Tools
@@ -87,6 +85,9 @@ class GlobalSystem(object):
87
85
 
88
86
  @classmethod
89
87
  def execute_command(cls, cmd, do_log_output=False, do_raise_on_stderr=False):
88
+ from holado_system.system.command.command import Command, CommandStates
89
+ from holado_system.system.command.command_result import CommandResult
90
+
90
91
  command = Command(cmd, do_log_output=do_log_output, do_raise_on_stderr=do_raise_on_stderr)
91
92
  command.start()
92
93
  command.join()
@@ -183,5 +184,4 @@ class GlobalSystem(object):
183
184
  return port
184
185
  return None
185
186
 
186
-
187
187
 
@@ -19,10 +19,12 @@ import logging
19
19
 
20
20
  # Add testing solution sources paths
21
21
  here = os.path.abspath(os.path.dirname(__file__))
22
- sys.path.append(here)
23
- holado_path = os.path.normpath(os.path.join(here, "..", "..", ".."))
24
- sys.path.append( os.path.join(holado_path, "src") )
25
- sys.path.append( os.path.join(holado_path, "tests", "behave") )
22
+ sys.path.insert(0, here)
23
+
24
+ # Add HolAdo source paths (needed when using a clone of HolAdo project)
25
+ from initialize_holado import insert_holado_source_paths
26
+ insert_holado_source_paths()
27
+
26
28
 
27
29
  # Configure HolAdo
28
30
  import holado