galaxy-web-stack 25.1.2__tar.gz → 26.0.1.dev0__tar.gz

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 (24) hide show
  1. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/HISTORY.rst +25 -0
  2. galaxy_web_stack-26.0.1.dev0/LICENSE +38 -0
  3. {galaxy_web_stack-25.1.2/galaxy_web_stack.egg-info → galaxy_web_stack-26.0.1.dev0}/PKG-INFO +31 -19
  4. galaxy_web_stack-26.0.1.dev0/dev-requirements.txt +6 -0
  5. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/galaxy/web_stack/__init__.py +19 -19
  6. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/galaxy/web_stack/handlers.py +120 -48
  7. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0/galaxy_web_stack.egg-info}/PKG-INFO +31 -19
  8. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/galaxy_web_stack.egg-info/SOURCES.txt +0 -1
  9. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/galaxy_web_stack.egg-info/requires.txt +4 -0
  10. galaxy_web_stack-26.0.1.dev0/pyproject.toml +16 -0
  11. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/setup.cfg +9 -5
  12. galaxy_web_stack-25.1.2/LICENSE +0 -228
  13. galaxy_web_stack-25.1.2/dev-requirements.txt +0 -10
  14. galaxy_web_stack-25.1.2/pyproject.toml +0 -3
  15. galaxy_web_stack-25.1.2/test-requirements.txt +0 -2
  16. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/MANIFEST.in +0 -0
  17. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/README.rst +0 -0
  18. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/galaxy/__init__.py +0 -0
  19. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/galaxy/py.typed +0 -0
  20. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/galaxy/web_stack/gunicorn_config.py +0 -0
  21. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/galaxy/web_stack/message.py +0 -0
  22. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/galaxy/web_stack/transport.py +0 -0
  23. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/galaxy_web_stack.egg-info/dependency_links.txt +0 -0
  24. {galaxy_web_stack-25.1.2 → galaxy_web_stack-26.0.1.dev0}/galaxy_web_stack.egg-info/top_level.txt +0 -0
@@ -3,6 +3,31 @@ History
3
3
 
4
4
  .. to_doc
5
5
 
6
+ -----------
7
+ 26.0.1.dev0
8
+ -----------
9
+
10
+
11
+
12
+ -------------------
13
+ 26.0.0 (2026-04-08)
14
+ -------------------
15
+
16
+
17
+ =========
18
+ Bug fixes
19
+ =========
20
+
21
+ * Fix differentiation between single handler and handler tag by `@natefoo <https://github.com/natefoo>`_ in `#21252 <https://github.com/galaxyproject/galaxy/pull/21252>`_
22
+
23
+ ============
24
+ Enhancements
25
+ ============
26
+
27
+ * Add type annotations to job handling code by `@nsoranzo <https://github.com/nsoranzo>`_ in `#21171 <https://github.com/galaxyproject/galaxy/pull/21171>`_
28
+ * Clean up code with pyupgrade by `@nsoranzo <https://github.com/nsoranzo>`_ in `#21540 <https://github.com/galaxyproject/galaxy/pull/21540>`_
29
+ * Drop support for Python 3.9 by `@nsoranzo <https://github.com/nsoranzo>`_ in `#21583 <https://github.com/galaxyproject/galaxy/pull/21583>`_
30
+
6
31
  -------------------
7
32
  25.1.2 (2026-03-09)
8
33
  -------------------
@@ -0,0 +1,38 @@
1
+ Copyright (c) 2005-2026 Galaxy Contributors (see CONTRIBUTORS.md)
2
+
3
+ Galaxy is provided from 2026-02-25 onwards entirely under the MIT License.
4
+
5
+ Some icons found in Galaxy are from the Silk Icons set, available under
6
+ the Creative Commons Attribution 2.5 License, from:
7
+
8
+ http://www.famfamfam.com/lab/icons/silk/
9
+
10
+
11
+ Other images and documentation are licensed under the Creative Commons
12
+ Attribution 3.0 (CC BY 3.0) License. See:
13
+
14
+ http://creativecommons.org/licenses/by/3.0/
15
+
16
+
17
+ --------------------------------------------------------------------------------
18
+
19
+
20
+ MIT License
21
+
22
+ Permission is hereby granted, free of charge, to any person obtaining a copy
23
+ of this software and associated documentation files (the "Software"), to deal
24
+ in the Software without restriction, including without limitation the rights
25
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
26
+ copies of the Software, and to permit persons to whom the Software is
27
+ furnished to do so, subject to the following conditions:
28
+
29
+ The above copyright notice and this permission notice shall be included in all
30
+ copies or substantial portions of the Software.
31
+
32
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38
+ SOFTWARE.
@@ -1,33 +1,20 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: galaxy-web-stack
3
- Version: 25.1.2
3
+ Version: 26.0.1.dev0
4
4
  Summary: Galaxy web stack abstraction
5
5
  Home-page: https://github.com/galaxyproject/galaxy
6
6
  Author: Galaxy Project and Community
7
7
  Author-email: galaxy-committers@lists.galaxyproject.org
8
- License: AFL
9
- Keywords: Galaxy
10
- Classifier: Development Status :: 5 - Production/Stable
11
- Classifier: Environment :: Console
12
- Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: Academic Free License (AFL)
14
- Classifier: Natural Language :: English
15
- Classifier: Operating System :: POSIX
16
- Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.9
18
- Classifier: Programming Language :: Python :: 3.10
19
- Classifier: Programming Language :: Python :: 3.11
20
- Classifier: Programming Language :: Python :: 3.12
21
- Classifier: Programming Language :: Python :: 3.13
22
- Classifier: Topic :: Software Development
23
- Classifier: Topic :: Software Development :: Code Generators
24
- Classifier: Topic :: Software Development :: Testing
25
- Requires-Python: >=3.9
8
+ License: MIT
9
+ Requires-Python: >=3.10
26
10
  Description-Content-Type: text/x-rst
27
11
  License-File: LICENSE
28
12
  Requires-Dist: galaxy-data
29
13
  Requires-Dist: galaxy-util
30
14
  Requires-Dist: SQLAlchemy!=2.0.41,<2.1,>=2.0.37
15
+ Provides-Extra: test
16
+ Requires-Dist: pytest; extra == "test"
17
+ Requires-Dist: gunicorn; extra == "test"
31
18
  Dynamic: license-file
32
19
 
33
20
 
@@ -49,6 +36,31 @@ History
49
36
 
50
37
  .. to_doc
51
38
 
39
+ -----------
40
+ 26.0.1.dev0
41
+ -----------
42
+
43
+
44
+
45
+ -------------------
46
+ 26.0.0 (2026-04-08)
47
+ -------------------
48
+
49
+
50
+ =========
51
+ Bug fixes
52
+ =========
53
+
54
+ * Fix differentiation between single handler and handler tag by `@natefoo <https://github.com/natefoo>`_ in `#21252 <https://github.com/galaxyproject/galaxy/pull/21252>`_
55
+
56
+ ============
57
+ Enhancements
58
+ ============
59
+
60
+ * Add type annotations to job handling code by `@nsoranzo <https://github.com/nsoranzo>`_ in `#21171 <https://github.com/galaxyproject/galaxy/pull/21171>`_
61
+ * Clean up code with pyupgrade by `@nsoranzo <https://github.com/nsoranzo>`_ in `#21540 <https://github.com/galaxyproject/galaxy/pull/21540>`_
62
+ * Drop support for Python 3.9 by `@nsoranzo <https://github.com/nsoranzo>`_ in `#21583 <https://github.com/galaxyproject/galaxy/pull/21583>`_
63
+
52
64
  -------------------
53
65
  25.1.2 (2026-03-09)
54
66
  -------------------
@@ -0,0 +1,6 @@
1
+ # For dev
2
+ mypy
3
+
4
+ # For release
5
+ build
6
+ twine==6.2.0
@@ -5,15 +5,19 @@ import multiprocessing
5
5
  import os
6
6
  import sys
7
7
  import threading
8
+ from collections.abc import Callable
8
9
  from typing import (
9
- Callable,
10
10
  Optional,
11
+ TYPE_CHECKING,
11
12
  )
12
13
 
13
14
  from galaxy.model import database_utils
14
15
  from galaxy.util.facts import get_facts
15
16
  from .handlers import HANDLER_ASSIGNMENT_METHODS
16
17
 
18
+ if TYPE_CHECKING:
19
+ from .handlers import ConfiguresHandlers
20
+
17
21
  log = logging.getLogger(__name__)
18
22
 
19
23
 
@@ -76,17 +80,17 @@ class ApplicationStack:
76
80
  self._preferred_handler_assignment_method = HANDLER_ASSIGNMENT_METHODS.DB_TRANSACTION_ISOLATION
77
81
  return self._preferred_handler_assignment_method
78
82
 
79
- def _set_default_job_handler_assignment_methods(self, job_config, base_pool):
83
+ def _set_default_job_handler_assignment_methods(self, job_config: "ConfiguresHandlers", base_pool: str) -> None:
80
84
  """Override in subclasses to set default job handler assignment methods if not explicitly configured by the administrator.
81
85
 
82
86
  Called once per job_config.
83
87
  """
84
88
 
85
- def _init_job_handler_assignment_methods(self, job_config, base_pool):
89
+ def _init_job_handler_assignment_methods(self, job_config: "ConfiguresHandlers", base_pool: str) -> None:
86
90
  if not job_config.handler_assignment_methods_configured:
87
91
  self._set_default_job_handler_assignment_methods(job_config, base_pool)
88
92
 
89
- def _init_job_handler_subpools(self, job_config, base_pool):
93
+ def _init_job_handler_subpools(self, job_config: "ConfiguresHandlers", base_pool: str) -> None:
90
94
  """Set up members of "subpools" ("base_pool.*") as handlers (including the base pool itself, if it exists)."""
91
95
  for pool_name in self.configured_pools:
92
96
  if pool_name == base_pool:
@@ -105,7 +109,7 @@ class ApplicationStack:
105
109
  job_config.add_handler(handler, [tag])
106
110
  job_config.pool_for_tag[tag] = pool_name
107
111
 
108
- def init_job_handling(self, job_config):
112
+ def init_job_handling(self, job_config: "ConfiguresHandlers") -> None:
109
113
  """Automatically add pools as handlers if they are named per predefined names and there is not an explicit
110
114
  job handler assignment configuration.
111
115
 
@@ -147,17 +151,17 @@ class ApplicationStack:
147
151
  def configured_pools(self):
148
152
  return {}
149
153
 
150
- def has_base_pool(self, pool_name):
154
+ def has_base_pool(self, pool_name: str) -> bool:
151
155
  return self.has_pool(pool_name) or any(pool.startswith(f"{pool_name}.") for pool in self.configured_pools)
152
156
 
153
- def has_pool(self, pool_name):
157
+ def has_pool(self, pool_name: str) -> bool:
154
158
  return pool_name in self.configured_pools
155
159
 
156
- def in_pool(self, pool_name):
160
+ def in_pool(self, pool_name: str) -> bool:
157
161
  return False
158
162
 
159
- def pool_members(self, pool_name):
160
- return None
163
+ def pool_members(self, pool_name: str) -> tuple[str, ...]:
164
+ return ()
161
165
 
162
166
  @property
163
167
  def facts(self):
@@ -224,7 +228,7 @@ class GunicornApplicationStack(ApplicationStack):
224
228
  class WeblessApplicationStack(ApplicationStack):
225
229
  name = "Webless"
226
230
 
227
- def _set_default_job_handler_assignment_methods(self, job_config, base_pool):
231
+ def _set_default_job_handler_assignment_methods(self, job_config: "ConfiguresHandlers", base_pool: str) -> None:
228
232
  # We will only get here if --attach-to-pool has been set so it is safe to assume that this handler is dynamic
229
233
  # and that we want to use one of the DB serialization methods.
230
234
  #
@@ -267,11 +271,11 @@ class WeblessApplicationStack(ApplicationStack):
267
271
  def configured_pools(self):
268
272
  return dict.fromkeys(self.config.attach_to_pools, self.config.server_name)
269
273
 
270
- def in_pool(self, pool_name):
274
+ def in_pool(self, pool_name: str) -> bool:
271
275
  return pool_name in self.config.attach_to_pools
272
276
 
273
- def pool_members(self, pool_name):
274
- return (self.config.server_name,) if self.in_pool(pool_name) else None
277
+ def pool_members(self, pool_name: str) -> tuple[str, ...]:
278
+ return (self.config.server_name,) if self.in_pool(pool_name) else ()
275
279
 
276
280
 
277
281
  def application_stack_class() -> type[ApplicationStack]:
@@ -294,13 +298,9 @@ def application_stack_log_filter():
294
298
  return application_stack_class().log_filter_class()
295
299
 
296
300
 
297
- def application_stack_log_formatter():
301
+ def application_stack_log_formatter() -> logging.Formatter:
298
302
  return logging.Formatter(fmt=application_stack_class().log_format)
299
303
 
300
304
 
301
- def register_postfork_function(f, *args, post_fork_only=False, **kwargs):
302
- application_stack_class().register_postfork_function(f, *args, post_fork_only=post_fork_only**kwargs)
303
-
304
-
305
305
  def get_app_kwds(config_section, app_name=None):
306
306
  return application_stack_class().get_app_kwds(config_section, app_name=app_name)
@@ -7,7 +7,21 @@ for some activity such as queuing up jobs or scheduling workflows.
7
7
  import logging
8
8
  import os
9
9
  import random
10
+ from collections.abc import (
11
+ Callable,
12
+ Iterable,
13
+ Sequence,
14
+ )
10
15
  from enum import Enum
16
+ from typing import (
17
+ Any,
18
+ Concatenate,
19
+ Literal,
20
+ Protocol,
21
+ TYPE_CHECKING,
22
+ TypeVar,
23
+ Union,
24
+ )
11
25
 
12
26
  from sqlalchemy.orm import object_session
13
27
 
@@ -15,8 +29,13 @@ from galaxy.exceptions import HandlerAssignmentError
15
29
  from galaxy.util import (
16
30
  ExecutionTimer,
17
31
  listify,
32
+ unicodify,
18
33
  )
19
34
 
35
+ if TYPE_CHECKING:
36
+ from galaxy.structured_app import MinimalManagerApp
37
+ from galaxy.util import Element
38
+
20
39
  log = logging.getLogger(__name__)
21
40
 
22
41
 
@@ -36,13 +55,29 @@ class HandlerAssignmentSkip(Exception):
36
55
  """Exception for handler assignment methods to raise if the next method should be tried."""
37
56
 
38
57
 
58
+ class ModelWithHandler(Protocol):
59
+ def log_str(self) -> str: ...
60
+
61
+ def set_handler(self, handler: str) -> None: ...
62
+
63
+
64
+ T = TypeVar("T")
65
+
66
+
39
67
  class ConfiguresHandlers:
40
68
  DEFAULT_HANDLER_TAG = "_default_"
41
69
  DEFAULT_BASE_HANDLER_POOLS: tuple[str, ...] = ()
42
70
 
43
- def add_handler(self, handler_id, tags):
71
+ def __init__(self, app: "MinimalManagerApp") -> None:
72
+ self.app = app
73
+ self.handler_assignment_methods: list[HANDLER_ASSIGNMENT_METHODS] = []
74
+ self.handler_assignment_methods_configured = False
75
+ self.handler_max_grab: Union[int, None] = None
76
+ self.handlers: dict[str, list[str]] = {}
77
+
78
+ def add_handler(self, handler_id: str, tags: list[str]) -> None:
44
79
  if handler_id not in self.handlers:
45
- self.handlers[handler_id] = (handler_id,)
80
+ self.handlers[handler_id] = [handler_id]
46
81
  for tag in tags:
47
82
  if tag in self.handlers and handler_id not in self.handlers[tag]:
48
83
  self.handlers[tag].append(handler_id)
@@ -50,23 +85,24 @@ class ConfiguresHandlers:
50
85
  self.handlers[tag] = [handler_id]
51
86
 
52
87
  @staticmethod
53
- def xml_to_dict(config, config_element):
54
- handling_config_dict = {}
55
-
56
- processes = {}
57
- handling_config_dict["processes"] = processes
88
+ def xml_to_dict(config, config_element: Union["Element", None]) -> dict[str, Any]:
89
+ processes: dict[str, dict[str, list[str]]] = {}
90
+ handling_config_dict: dict[str, Any] = {"processes": processes}
58
91
 
59
92
  # Parse handlers
60
93
  if config_element is not None:
61
94
  for handler in ConfiguresHandlers._findall_with_required(config_element, "handler"):
62
95
  handler_id = handler.get("id")
96
+ assert handler_id is not None # guaranteed by _findall_with_required()
63
97
  if handler_id in processes:
64
98
  log.error("Handler '%s' overlaps handler with the same name, ignoring", handler_id)
65
99
  else:
66
100
  log.debug("Read definition for handler '%s'", handler_id)
67
101
  plugins = []
68
102
  for plugin in ConfiguresHandlers._findall_with_required(handler, "plugin", ["id"]):
69
- plugins.append(plugin.get("id"))
103
+ plugin_id = plugin.get("id")
104
+ assert plugin_id is not None # guaranteed by _findall_with_required()
105
+ plugins.append(plugin_id)
70
106
  tags = [x.strip() for x in handler.get("tags", ConfiguresHandlers.DEFAULT_HANDLER_TAG).split(",")]
71
107
  handler_def = {"tags": tags}
72
108
  if plugins:
@@ -88,7 +124,7 @@ class ConfiguresHandlers:
88
124
 
89
125
  return handling_config_dict
90
126
 
91
- def _init_handlers(self, handling_config_dict=None):
127
+ def _init_handlers(self, handling_config_dict: Union[dict, None]) -> None:
92
128
  handling_config_dict = handling_config_dict or {}
93
129
  for handler_id, process in handling_config_dict.get("processes", {}).items():
94
130
  process = process or {}
@@ -103,13 +139,16 @@ class ConfiguresHandlers:
103
139
  handling_config_dict.get("default"), list(self.handlers.keys()), required=False
104
140
  )
105
141
 
106
- def _init_handler_assignment_methods(self, handling_config_dict=None):
142
+ def _init_handler_assignment_methods(self, handling_config_dict: Union[dict, None] = None) -> None:
107
143
  handling_config_dict = handling_config_dict or {}
108
144
 
109
- self.__is_handler = None
145
+ self.__is_handler: Union[bool, None] = None
110
146
  # This is set by the stack job handler init code
111
- self.pool_for_tag = {}
112
- self._handler_assignment_method_methods = {
147
+ self.pool_for_tag: dict[str, str] = {}
148
+ self._handler_assignment_method_methods: dict[
149
+ HANDLER_ASSIGNMENT_METHODS,
150
+ Callable[Concatenate[ModelWithHandler, HANDLER_ASSIGNMENT_METHODS, Union[str, None], bool, ...], str],
151
+ ] = {
113
152
  HANDLER_ASSIGNMENT_METHODS.MEM_SELF: self._assign_mem_self_handler,
114
153
  HANDLER_ASSIGNMENT_METHODS.DB_SELF: self._assign_db_self_handler,
115
154
  HANDLER_ASSIGNMENT_METHODS.DB_PREASSIGN: self._assign_db_preassign_handler,
@@ -124,18 +163,14 @@ class ConfiguresHandlers:
124
163
  ), "Invalid job handler assignment method '{}', must be one of: {}".format(
125
164
  method, ", ".join(h.value for h in HANDLER_ASSIGNMENT_METHODS)
126
165
  )
127
- try:
128
- self.handler_assignment_methods.append(method)
129
- except AttributeError:
130
- self.handler_assignment_methods_configured = True
131
- self.handler_assignment_methods = [method]
166
+ self.handler_assignment_methods.append(method)
167
+ self.handler_assignment_methods_configured = True
132
168
  if self.handler_assignment_methods == [HANDLER_ASSIGNMENT_METHODS.MEM_SELF]:
133
169
  self.app.config.track_jobs_in_database = False
134
- self.handler_max_grab = handling_config_dict.get("max_grab", self.handler_max_grab)
135
- if self.handler_max_grab is not None:
136
- self.handler_max_grab = int(self.handler_max_grab)
170
+ if (max_grab := handling_config_dict.get("max_grab")) is not None:
171
+ self.handler_max_grab = int(max_grab)
137
172
 
138
- def _set_default_handler_assignment_methods(self):
173
+ def _set_default_handler_assignment_methods(self) -> None:
139
174
  if not self.handler_assignment_methods_configured:
140
175
  if not self.app.config.track_jobs_in_database:
141
176
  # DEPRECATED: You should just set mem_self as the only method if you want this
@@ -160,17 +195,19 @@ class ConfiguresHandlers:
160
195
  pass
161
196
 
162
197
  @staticmethod
163
- def get_xml_default(config, parent):
198
+ def get_xml_default(config, parent: "Element"):
164
199
  rval = parent.get("default")
165
200
  if "default_from_environ" in parent.attrib:
166
- environ_var = parent.attrib["default_from_environ"]
201
+ environ_var = unicodify(parent.attrib["default_from_environ"])
167
202
  rval = os.environ.get(environ_var, rval)
168
203
  elif "default_from_config" in parent.attrib:
169
- config_val = parent.attrib["default_from_config"]
204
+ config_val = unicodify(parent.attrib["default_from_config"])
170
205
  rval = config.config_dict.get(config_val, rval)
171
206
  return rval
172
207
 
173
- def _get_default(self, config, parent, names, auto=False, required=True):
208
+ def _get_default(
209
+ self, config, parent: "Element", names: list[str], auto: bool = False, required: bool = True
210
+ ) -> Union[str, None]:
174
211
  """
175
212
  Returns the default attribute set in a parent tag like <handlers> or
176
213
  <destinations>, or return the ID of the child, if there is no explicit
@@ -190,7 +227,9 @@ class ConfiguresHandlers:
190
227
  rval = ConfiguresHandlers.get_xml_default(config, parent)
191
228
  return self._ensure_default_set(rval, names, auto=auto, required=required)
192
229
 
193
- def _ensure_default_set(self, rval, names, auto=False, required=True):
230
+ def _ensure_default_set(
231
+ self, rval: Union[str, None], names: list[str], auto: bool = False, required: bool = True
232
+ ) -> Union[str, None]:
194
233
  if rval is not None:
195
234
  # If the parent element has a 'default' attribute, use the id or tag in that attribute
196
235
  if required and rval not in names:
@@ -204,7 +243,9 @@ class ConfiguresHandlers:
204
243
  return rval
205
244
 
206
245
  @staticmethod
207
- def _findall_with_required(parent, match, attribs=None):
246
+ def _findall_with_required(
247
+ parent: "Element", match: str, attribs: Union[Iterable[str], None] = None
248
+ ) -> list["Element"]:
208
249
  """Like ``lxml.etree.Element.findall()``, except only returns children that have the specified attribs.
209
250
 
210
251
  :param parent: Parent element in which to find.
@@ -216,7 +257,7 @@ class ConfiguresHandlers:
216
257
 
217
258
  :returns: list of ``lxml.etree._Element``
218
259
  """
219
- rval = []
260
+ rval: list[Element] = []
220
261
  if attribs is None:
221
262
  attribs = ("id",)
222
263
  for elem in parent.findall(match):
@@ -234,7 +275,7 @@ class ConfiguresHandlers:
234
275
  filter(lambda x: x == HANDLER_ASSIGNMENT_METHODS.DB_PREASSIGN), self.handler_assignment_methods
235
276
  )
236
277
 
237
- def _get_is_handler(self):
278
+ def _get_is_handler(self) -> bool:
238
279
  """Indicate whether the current server is configured as a handler.
239
280
 
240
281
  :return: bool
@@ -260,12 +301,12 @@ class ConfiguresHandlers:
260
301
  return True
261
302
  return False
262
303
 
263
- def _set_is_handler(self, value):
304
+ def _set_is_handler(self, value: bool) -> None:
264
305
  self.__is_handler = value
265
306
 
266
307
  is_handler = property(_get_is_handler, _set_is_handler)
267
308
 
268
- def _get_single_item(self, collection, index=None):
309
+ def _get_single_item(self, collection: Sequence[T], index: Union[int, None] = None) -> T:
269
310
  """Given a collection of handlers or destinations, return one item from the collection at random."""
270
311
  # Done like this to avoid random under the assumption it's faster to avoid it
271
312
  if len(collection) == 1:
@@ -278,7 +319,7 @@ class ConfiguresHandlers:
278
319
  @property
279
320
  def handler_tags(self):
280
321
  """Get an iterable of all configured handler tags."""
281
- return filter(lambda k: isinstance(self.handlers[k], list), self.handlers.keys())
322
+ return filter(lambda k: self.handlers[k] != [k], self.handlers.keys())
282
323
 
283
324
  @property
284
325
  def self_handler_tags(self):
@@ -289,7 +330,9 @@ class ConfiguresHandlers:
289
330
 
290
331
  # If these get to be any more complex we should probably modularize them, or at least move to a separate class
291
332
 
292
- def _assign_handler_direct(self, obj, configured, flush=True):
333
+ def _assign_handler_direct(
334
+ self, obj: ModelWithHandler, configured: Union[str, None], flush: bool = True
335
+ ) -> Union[str, Literal[False]]:
293
336
  """Directly assign a handler if the object has been preconfigured to a known single static handler.
294
337
 
295
338
  :param obj: Same as :method:`ConfiguresHandlers.assign_handler()`.
@@ -302,14 +345,22 @@ class ConfiguresHandlers:
302
345
  handlers = self.handlers[configured]
303
346
  except KeyError:
304
347
  handlers = None
305
- if handlers == (configured,):
348
+ if handlers == [configured]:
306
349
  obj.set_handler(configured)
307
350
  if flush:
308
351
  _timed_flush_obj(obj)
309
352
  return configured
310
353
  return False
311
354
 
312
- def _assign_mem_self_handler(self, obj, method, configured, queue_callback=None, flush=True, **kwargs):
355
+ def _assign_mem_self_handler(
356
+ self,
357
+ obj: ModelWithHandler,
358
+ method: HANDLER_ASSIGNMENT_METHODS,
359
+ configured: Union[str, None],
360
+ flush: bool,
361
+ queue_callback=None,
362
+ **kwargs,
363
+ ) -> str:
313
364
  """Assign object to this handler using this process's in-memory queue.
314
365
 
315
366
  This method ignores all handler configuration.
@@ -339,7 +390,14 @@ class ConfiguresHandlers:
339
390
  queue_callback()
340
391
  return self.app.config.server_name
341
392
 
342
- def _assign_db_self_handler(self, obj, method, configured, flush=True, **kwargs):
393
+ def _assign_db_self_handler(
394
+ self,
395
+ obj: ModelWithHandler,
396
+ method: HANDLER_ASSIGNMENT_METHODS,
397
+ configured: Union[str, None],
398
+ flush: bool,
399
+ **kwargs,
400
+ ) -> str:
343
401
  """Assign object to this process by setting its ``handler`` column in the database to this process.
344
402
 
345
403
  This only occurs if there is not an explicitly configured handler assignment for the object. Otherwise, it is
@@ -353,14 +411,22 @@ class ConfiguresHandlers:
353
411
  """
354
412
  if configured:
355
413
  return self._handler_assignment_method_methods[HANDLER_ASSIGNMENT_METHODS.DB_PREASSIGN](
356
- obj, method, configured, **kwargs
414
+ obj, method, configured, flush, **kwargs
357
415
  )
358
416
  obj.set_handler(self.app.config.server_name)
359
417
  if flush:
360
418
  _timed_flush_obj(obj)
361
419
  return self.app.config.server_name
362
420
 
363
- def _assign_db_preassign_handler(self, obj, method, configured, index=None, flush=True, **kwargs):
421
+ def _assign_db_preassign_handler(
422
+ self,
423
+ obj: ModelWithHandler,
424
+ method: HANDLER_ASSIGNMENT_METHODS,
425
+ configured: Union[str, None],
426
+ flush: bool,
427
+ index: Union[int, None] = None,
428
+ **kwargs,
429
+ ) -> str:
364
430
  """Assign object to a handler by setting its ``handler`` column in the database to a handler selected at random
365
431
  from the known handlers in the appropriate tag.
366
432
 
@@ -394,7 +460,14 @@ class ConfiguresHandlers:
394
460
  _timed_flush_obj(obj)
395
461
  return handler_id
396
462
 
397
- def _assign_db_tag(self, obj, method, configured, flush=True, **kwargs):
463
+ def _assign_db_tag(
464
+ self,
465
+ obj: ModelWithHandler,
466
+ method: HANDLER_ASSIGNMENT_METHODS,
467
+ configured: Union[str, None],
468
+ flush: bool,
469
+ **kwargs,
470
+ ) -> str:
398
471
  """Assign object to a handler by setting its ``handler`` column in the database to either the configured handler
399
472
  ID or tag, or to the default tag (or ``_default_``)
400
473
 
@@ -412,34 +485,33 @@ class ConfiguresHandlers:
412
485
  _timed_flush_obj(obj)
413
486
  return handler
414
487
 
415
- def assign_handler(self, obj, configured=None, **kwargs):
488
+ def assign_handler(self, obj: ModelWithHandler, configured: Union[str, None] = None, flush: bool = True, **kwargs):
416
489
  """Set a job handler, flush obj
417
490
 
418
491
  Called assignment methods should raise py:class:`HandlerAssignmentSkip` to indicate that the next method
419
492
  should be tried.
420
493
 
421
- :param obj: Object to assign a handler to (must be a model object with ``handler`` attribute and
422
- ``log_str`` callable).
423
- :type obj: instance of :class:`galaxy.model.Job` or other model object with a ``set_handler()`` method.
494
+ :param obj: Model object to assign a handler to.
495
+ :type obj: model object with ``set_handler()`` and ``log_str()`` methods.
424
496
  :param configured: Preconfigured handler (ID, tag, or None) for the given object.
425
497
  :type configured: str or None.
426
498
 
427
- :returns: bool -- True on successful assignment, False otherwise.
499
+ :returns: str -- The assigned handler ID or tag.
428
500
  """
429
501
  # It's a bit awkward that the method that actually hands off job execution is in the JobConfiguration, but
430
502
  # that's currently the best place for it. It's worth noting that this method is also part of the
431
503
  # WorkflowSchedulingManager, which acts like a combined JobConfiguration and JobManager. Combining those two
432
504
  # classes would probably be reasonable (and would remove the need for the queue callback).
433
- if self._assign_handler_direct(obj, configured, flush=kwargs.get("flush", True)):
505
+ if handler := self._assign_handler_direct(obj, configured, flush=flush):
434
506
  log.info(
435
507
  "(%s) Skipped handler assignment logic due to explicit configuration` to a single handler: %s",
436
508
  obj.log_str(),
437
509
  configured,
438
510
  )
439
- return True
511
+ return handler
440
512
  for method in self.handler_assignment_methods:
441
513
  try:
442
- handler = self._handler_assignment_method_methods[method](obj, method, configured=configured, **kwargs)
514
+ handler = self._handler_assignment_method_methods[method](obj, method, configured, flush, **kwargs)
443
515
  log.info("(%s) Handler '%s' assigned using '%s' assignment method", obj.log_str(), handler, method)
444
516
  return handler
445
517
  except HandlerAssignmentSkip:
@@ -1,33 +1,20 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: galaxy-web-stack
3
- Version: 25.1.2
3
+ Version: 26.0.1.dev0
4
4
  Summary: Galaxy web stack abstraction
5
5
  Home-page: https://github.com/galaxyproject/galaxy
6
6
  Author: Galaxy Project and Community
7
7
  Author-email: galaxy-committers@lists.galaxyproject.org
8
- License: AFL
9
- Keywords: Galaxy
10
- Classifier: Development Status :: 5 - Production/Stable
11
- Classifier: Environment :: Console
12
- Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: Academic Free License (AFL)
14
- Classifier: Natural Language :: English
15
- Classifier: Operating System :: POSIX
16
- Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.9
18
- Classifier: Programming Language :: Python :: 3.10
19
- Classifier: Programming Language :: Python :: 3.11
20
- Classifier: Programming Language :: Python :: 3.12
21
- Classifier: Programming Language :: Python :: 3.13
22
- Classifier: Topic :: Software Development
23
- Classifier: Topic :: Software Development :: Code Generators
24
- Classifier: Topic :: Software Development :: Testing
25
- Requires-Python: >=3.9
8
+ License: MIT
9
+ Requires-Python: >=3.10
26
10
  Description-Content-Type: text/x-rst
27
11
  License-File: LICENSE
28
12
  Requires-Dist: galaxy-data
29
13
  Requires-Dist: galaxy-util
30
14
  Requires-Dist: SQLAlchemy!=2.0.41,<2.1,>=2.0.37
15
+ Provides-Extra: test
16
+ Requires-Dist: pytest; extra == "test"
17
+ Requires-Dist: gunicorn; extra == "test"
31
18
  Dynamic: license-file
32
19
 
33
20
 
@@ -49,6 +36,31 @@ History
49
36
 
50
37
  .. to_doc
51
38
 
39
+ -----------
40
+ 26.0.1.dev0
41
+ -----------
42
+
43
+
44
+
45
+ -------------------
46
+ 26.0.0 (2026-04-08)
47
+ -------------------
48
+
49
+
50
+ =========
51
+ Bug fixes
52
+ =========
53
+
54
+ * Fix differentiation between single handler and handler tag by `@natefoo <https://github.com/natefoo>`_ in `#21252 <https://github.com/galaxyproject/galaxy/pull/21252>`_
55
+
56
+ ============
57
+ Enhancements
58
+ ============
59
+
60
+ * Add type annotations to job handling code by `@nsoranzo <https://github.com/nsoranzo>`_ in `#21171 <https://github.com/galaxyproject/galaxy/pull/21171>`_
61
+ * Clean up code with pyupgrade by `@nsoranzo <https://github.com/nsoranzo>`_ in `#21540 <https://github.com/galaxyproject/galaxy/pull/21540>`_
62
+ * Drop support for Python 3.9 by `@nsoranzo <https://github.com/nsoranzo>`_ in `#21583 <https://github.com/galaxyproject/galaxy/pull/21583>`_
63
+
52
64
  -------------------
53
65
  25.1.2 (2026-03-09)
54
66
  -------------------
@@ -5,7 +5,6 @@ README.rst
5
5
  dev-requirements.txt
6
6
  pyproject.toml
7
7
  setup.cfg
8
- test-requirements.txt
9
8
  galaxy/__init__.py
10
9
  galaxy/py.typed
11
10
  galaxy/web_stack/__init__.py
@@ -1,3 +1,7 @@
1
1
  galaxy-data
2
2
  galaxy-util
3
3
  SQLAlchemy!=2.0.41,<2.1,>=2.0.37
4
+
5
+ [test]
6
+ pytest
7
+ gunicorn
@@ -0,0 +1,16 @@
1
+ [build-system]
2
+ build-backend = "setuptools.build_meta"
3
+ requires = ["setuptools"]
4
+
5
+ [project]
6
+ dynamic = [
7
+ "authors",
8
+ "dependencies",
9
+ "description",
10
+ "license",
11
+ "optional-dependencies",
12
+ "readme",
13
+ "requires-python",
14
+ "version",
15
+ ]
16
+ name = "galaxy-web-stack"
@@ -5,29 +5,28 @@ classifiers =
5
5
  Development Status :: 5 - Production/Stable
6
6
  Environment :: Console
7
7
  Intended Audience :: Developers
8
- License :: OSI Approved :: Academic Free License (AFL)
9
8
  Natural Language :: English
10
9
  Operating System :: POSIX
11
10
  Programming Language :: Python :: 3
12
- Programming Language :: Python :: 3.9
13
11
  Programming Language :: Python :: 3.10
14
12
  Programming Language :: Python :: 3.11
15
13
  Programming Language :: Python :: 3.12
16
14
  Programming Language :: Python :: 3.13
15
+ Programming Language :: Python :: 3.14
17
16
  Topic :: Software Development
18
17
  Topic :: Software Development :: Code Generators
19
18
  Topic :: Software Development :: Testing
20
19
  description = Galaxy web stack abstraction
21
20
  keywords =
22
21
  Galaxy
23
- license = AFL
22
+ license = MIT
24
23
  license_files =
25
24
  LICENSE
26
25
  long_description = file: README.rst, HISTORY.rst
27
26
  long_description_content_type = text/x-rst
28
27
  name = galaxy-web-stack
29
28
  url = https://github.com/galaxyproject/galaxy
30
- version = 25.1.2
29
+ version = 26.0.1.dev0
31
30
 
32
31
  [options]
33
32
  include_package_data = True
@@ -36,7 +35,12 @@ install_requires =
36
35
  galaxy-util
37
36
  SQLAlchemy>=2.0.37,<2.1,!=2.0.41
38
37
  packages = find:
39
- python_requires = >=3.9
38
+ python_requires = >=3.10
39
+
40
+ [options.extras_require]
41
+ test =
42
+ pytest
43
+ gunicorn
40
44
 
41
45
  [options.packages.find]
42
46
  exclude =
@@ -1,228 +0,0 @@
1
- Copyright (c) 2005-2022 Galaxy Contributors (see CONTRIBUTORS.md)
2
-
3
- Work contributed from 2021-04-07 onwards is licensed under the MIT License.
4
- Work contributed before this date is licensed under the Academic Free License
5
- version 3.0.
6
- See https://github.com/galaxyproject/galaxy/ for the contribution history.
7
- See below for the full text of both licenses.
8
-
9
-
10
- Some icons found in Galaxy are from the Silk Icons set, available under
11
- the Creative Commons Attribution 2.5 License, from:
12
-
13
- http://www.famfamfam.com/lab/icons/silk/
14
-
15
-
16
- Other images and documentation are licensed under the Creative Commons
17
- Attribution 3.0 (CC BY 3.0) License. See:
18
-
19
- http://creativecommons.org/licenses/by/3.0/
20
-
21
-
22
- --------------------------------------------------------------------------------
23
-
24
-
25
- MIT License
26
-
27
- Permission is hereby granted, free of charge, to any person obtaining a copy
28
- of this software and associated documentation files (the "Software"), to deal
29
- in the Software without restriction, including without limitation the rights
30
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31
- copies of the Software, and to permit persons to whom the Software is
32
- furnished to do so, subject to the following conditions:
33
-
34
- The above copyright notice and this permission notice shall be included in all
35
- copies or substantial portions of the Software.
36
-
37
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
43
- SOFTWARE.
44
-
45
-
46
- --------------------------------------------------------------------------------
47
-
48
-
49
- Academic Free License ("AFL") v. 3.0
50
-
51
- This Academic Free License (the "License") applies to any original work of
52
- authorship (the "Original Work") whose owner (the "Licensor") has placed the
53
- following licensing notice adjacent to the copyright notice for the Original
54
- Work:
55
-
56
- Licensed under the Academic Free License version 3.0
57
-
58
- 1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free,
59
- non-exclusive, sublicensable license, for the duration of the copyright, to
60
- do the following:
61
-
62
- a) to reproduce the Original Work in copies, either alone or as part of a
63
- collective work;
64
-
65
- b) to translate, adapt, alter, transform, modify, or arrange the Original
66
- Work, thereby creating derivative works ("Derivative Works") based upon
67
- the Original Work;
68
-
69
- c) to distribute or communicate copies of the Original Work and Derivative
70
- Works to the public, under any license of your choice that does not
71
- contradict the terms and conditions, including Licensor's reserved
72
- rights and remedies, in this Academic Free License;
73
-
74
- d) to perform the Original Work publicly; and
75
-
76
- e) to display the Original Work publicly.
77
-
78
- 2) Grant of Patent License. Licensor grants You a worldwide, royalty-free,
79
- non-exclusive, sublicensable license, under patent claims owned or
80
- controlled by the Licensor that are embodied in the Original Work as
81
- furnished by the Licensor, for the duration of the patents, to make, use,
82
- sell, offer for sale, have made, and import the Original Work and
83
- Derivative Works.
84
-
85
- 3) Grant of Source Code License. The term "Source Code" means the preferred
86
- form of the Original Work for making modifications to it and all available
87
- documentation describing how to modify the Original Work. Licensor agrees
88
- to provide a machine-readable copy of the Source Code of the Original Work
89
- along with each copy of the Original Work that Licensor distributes.
90
- Licensor reserves the right to satisfy this obligation by placing a
91
- machine-readable copy of the Source Code in an information repository
92
- reasonably calculated to permit inexpensive and convenient access by You
93
- for as long as Licensor continues to distribute the Original Work.
94
-
95
- 4) Exclusions From License Grant. Neither the names of Licensor, nor the
96
- names of any contributors to the Original Work, nor any of their
97
- trademarks or service marks, may be used to endorse or promote products
98
- derived from this Original Work without express prior permission of the
99
- Licensor. Except as expressly stated herein, nothing in this License
100
- grants any license to Licensor's trademarks, copyrights, patents, trade
101
- secrets or any other intellectual property. No patent license is granted
102
- to make, use, sell, offer for sale, have made, or import embodiments of
103
- any patent claims other than the licensed claims defined in Section 2.
104
- No license is granted to the trademarks of Licensor even if such marks
105
- are included in the Original Work. Nothing in this License shall be
106
- interpreted to prohibit Licensor from licensing under terms different
107
- from this License any Original Work that Licensor otherwise would have a
108
- right to license.
109
-
110
- 5) External Deployment. The term "External Deployment" means the use,
111
- distribution, or communication of the Original Work or Derivative Works
112
- in any way such that the Original Work or Derivative Works may be used by
113
- anyone other than You, whether those works are distributed or
114
- communicated to those persons or made available as an application
115
- intended for use over a network. As an express condition for the grants
116
- of license hereunder, You must treat any External Deployment by You of
117
- the Original Work or a Derivative Work as a distribution under
118
- section 1(c).
119
-
120
- 6) Attribution Rights. You must retain, in the Source Code of any Derivative
121
- Works that You create, all copyright, patent, or trademark notices from
122
- the Source Code of the Original Work, as well as any notices of licensing
123
- and any descriptive text identified therein as an "Attribution Notice."
124
- You must cause the Source Code for any Derivative Works that You create
125
- to carry a prominent Attribution Notice reasonably calculated to inform
126
- recipients that You have modified the Original Work.
127
-
128
- 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
129
- the copyright in and to the Original Work and the patent rights granted
130
- herein by Licensor are owned by the Licensor or are sublicensed to You
131
- under the terms of this License with the permission of the contributor(s)
132
- of those copyrights and patent rights. Except as expressly stated in the
133
- immediately preceding sentence, the Original Work is provided under this
134
- License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or
135
- implied, including, without limitation, the warranties of
136
- non-infringement, merchantability or fitness for a particular purpose.
137
- THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This
138
- DISCLAIMER OF WARRANTY constitutes an essential part of this License.
139
- No license to the Original Work is granted by this License except under
140
- this disclaimer.
141
-
142
- 8) Limitation of Liability. Under no circumstances and under no legal
143
- theory, whether in tort (including negligence), contract, or otherwise,
144
- shall the Licensor be liable to anyone for any indirect, special,
145
- incidental, or consequential damages of any character arising as a result
146
- of this License or the use of the Original Work including, without
147
- limitation, damages for loss of goodwill, work stoppage, computer failure
148
- or malfunction, or any and all other commercial damages or losses. This
149
- limitation of liability shall not apply to the extent applicable law
150
- prohibits such limitation.
151
-
152
- 9) Acceptance and Termination. If, at any time, You expressly assented to
153
- this License, that assent indicates your clear and irrevocable acceptance
154
- of this License and all of its terms and conditions. If You distribute or
155
- communicate copies of the Original Work or a Derivative Work, You must
156
- make a reasonable effort under the circumstances to obtain the express
157
- assent of recipients to the terms of this License. This License
158
- conditions your rights to undertake the activities listed in Section 1,
159
- including your right to create Derivative Works based upon the Original
160
- Work, and doing so without honoring these terms and conditions is
161
- prohibited by copyright law and international treaty. Nothing in this
162
- License is intended to affect copyright exceptions and limitations
163
- (including "fair use" or "fair dealing"). This License shall terminate
164
- immediately and You may no longer exercise any of the rights granted to
165
- You by this License upon your failure to honor the conditions in
166
- Section 1(c).
167
-
168
- 10) Termination for Patent Action. This License shall terminate
169
- automatically and You may no longer exercise any of the rights granted
170
- to You by this License as of the date You commence an action, including
171
- a cross-claim or counterclaim, against Licensor or any licensee alleging
172
- that the Original Work infringes a patent. This termination provision
173
- shall not apply for an action alleging patent infringement by
174
- combinations of the Original Work with other software or hardware.
175
-
176
- 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to
177
- this License may be brought only in the courts of a jurisdiction wherein
178
- the Licensor resides or in which Licensor conducts its primary business,
179
- and under the laws of that jurisdiction excluding its conflict-of-law
180
- provisions. The application of the United Nations Convention on
181
- Contracts for the International Sale of Goods is expressly excluded. Any
182
- use of the Original Work outside the scope of this License or after its
183
- termination shall be subject to the requirements and penalties of
184
- copyright or patent law in the appropriate jurisdiction. This section
185
- shall survive the termination of this License.
186
-
187
- 12) Attorneys' Fees. In any action to enforce the terms of this License or
188
- seeking damages relating thereto, the prevailing party shall be entitled
189
- to recover its costs and expenses, including, without limitation,
190
- reasonable attorneys' fees and costs incurred in connection with such
191
- action, including any appeal of such action. This section shall survive
192
- the termination of this License.
193
-
194
- 13) Miscellaneous. If any provision of this License is held to be
195
- unenforceable, such provision shall be reformed only to the extent
196
- necessary to make it enforceable.
197
-
198
- 14) Definition of "You" in This License. "You" throughout this License,
199
- whether in upper or lower case, means an individual or a legal entity
200
- exercising rights under, and complying with all of the terms of, this
201
- License. For legal entities, "You" includes any entity that controls, is
202
- controlled by, or is under common control with you. For purposes of this
203
- definition, "control" means (i) the power, direct or indirect, to cause
204
- the direction or management of such entity, whether by contract or
205
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
206
- outstanding shares, or (iii) beneficial ownership of such entity.
207
-
208
- 15) Right to Use. You may use the Original Work in all ways not otherwise
209
- restricted or conditioned by this License or by law, and Licensor
210
- promises not to interfere with or be responsible for such uses by You.
211
-
212
- 16) Modification of This License. This License is Copyright © 2005 Lawrence
213
- Rosen. Permission is granted to copy, distribute, or communicate this
214
- License without modification. Nothing in this License permits You to
215
- modify this License as applied to the Original Work or to Derivative
216
- Works. However, You may modify the text of this License and copy,
217
- distribute or communicate your modified version (the "Modified
218
- License") and apply it to other original works of authorship subject to
219
- the following conditions: (i) You may not indicate in any way that your
220
- Modified License is the "Academic Free License" or "AFL" and you may not
221
- use those names in the name of your Modified License; (ii) You must
222
- replace the notice specified in the first paragraph above with the
223
- notice "Licensed under <insert your license name here>" or with a notice
224
- of your own that is not confusingly similar to the notice in this
225
- License; and (iii) You may not claim that your original works are open
226
- source software unless your Modified License has been approved by Open
227
- Source Initiative (OSI) and You comply with its license review and
228
- certification process.
@@ -1,10 +0,0 @@
1
- # For testing
2
- -r test-requirements.txt
3
-
4
- # For dev
5
- sphinx
6
- mypy
7
-
8
- # For release
9
- build
10
- twine
@@ -1,3 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools"]
3
- build-backend = "setuptools.build_meta"
@@ -1,2 +0,0 @@
1
- gunicorn
2
- pytest