@qtoggle/qui 1.17.1 → 1.18.0

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.
@@ -3,53 +3,27 @@ name: Main
3
3
  on: push
4
4
 
5
5
  jobs:
6
-
7
- eslint:
8
- name: ESLint
9
- runs-on: ubuntu-latest
10
- steps:
11
- - name: Source code checkout
12
- uses: actions/checkout@v3
13
- - name: Node setup
14
- uses: actions/setup-node@v3
15
- with:
16
- node-version: 18
17
- - name: Install dev deps
18
- run: npm i --include=dev
19
- - name: ESLint
20
- run: npx eslint js
21
-
22
- flake8:
23
- name: Flake8
6
+ lint:
7
+ name: Lint
24
8
  runs-on: ubuntu-latest
25
9
  steps:
26
- - name: Source code checkout
27
- uses: actions/checkout@v3
28
- - name: Python setup
29
- uses: actions/setup-python@v4
10
+ - name: Lint JS Code
11
+ uses: qtoggle/actions-common/actions/lint-js@v1
12
+ - name: Lint Python Code
13
+ uses: qtoggle/actions-common/actions/lint-python@v1
30
14
  with:
31
- python-version: '3.x'
32
- - name: Install dev deps
33
- run: pip install flake8 flake8-annotations
34
- - name: Flake8
35
- run: flake8 qui
36
-
15
+ source-folder: qui
37
16
  jsdoc:
38
17
  name: Publish Docs
39
18
  if: startsWith(github.ref, 'refs/tags/version-')
40
19
  runs-on: ubuntu-latest
41
20
  needs:
42
- - eslint
21
+ - lint
43
22
  steps:
44
23
  - name: Source code checkout
45
- uses: actions/checkout@v3
46
- - name: Extract version from tag
47
- id: tagName
48
- uses: little-core-labs/get-git-tag@v3.0.2
49
- with:
50
- tagRegex: "version-(.*)"
51
- - name: Update source version
52
- run: sed -i "s/0.0.0-unknown.0/${{ steps.tagName.outputs.tag }}/" package.json
24
+ uses: actions/checkout@v4
25
+ - name: Replace source version
26
+ uses: qtoggle/actions-common/actions/replace-source-version@v1
53
27
  - name: Install
54
28
  run: |
55
29
  npm install && rm -rf docs/* js/lib/* && npx jsdoc -c jsdoc.conf.json
@@ -59,26 +33,27 @@ jobs:
59
33
  ACCESS_TOKEN: ${{ secrets.GH_PAGES_ACCESS_TOKEN }}
60
34
  BRANCH: gh-pages
61
35
  FOLDER: docs
62
-
36
+ build-python:
37
+ name: Build Python
38
+ runs-on: ubuntu-latest
39
+ steps:
40
+ - name: Build Python package
41
+ uses: qtoggle/actions-common/actions/build-python-package@v1
63
42
  publish:
64
43
  name: Publish
65
44
  if: startsWith(github.ref, 'refs/tags/version-')
66
45
  runs-on: ubuntu-latest
67
46
  needs:
68
- - eslint
47
+ - lint
69
48
  - jsdoc
49
+ - build-python
70
50
  steps:
71
51
  - name: Source code checkout
72
- uses: actions/checkout@v3
73
- - name: Extract version from tag
74
- id: tagName
75
- uses: little-core-labs/get-git-tag@v3.0.2
76
- with:
77
- tagRegex: "version-(.*)"
78
- - name: Update source version
79
- run: sed -i "s/0.0.0-unknown.0/${{ steps.tagName.outputs.tag }}/" package.json setup.py
80
- - name: Node setup
81
- uses: actions/setup-node@v3
52
+ uses: actions/checkout@v4
53
+ - name: Replace source version
54
+ uses: qtoggle/actions-common/actions/replace-source-version@v1
55
+ - name: Setup NodeJS
56
+ uses: actions/setup-node@v4
82
57
  with:
83
58
  node-version: 18
84
59
  - name: Publish to NPM
@@ -87,13 +62,12 @@ jobs:
87
62
  npm publish
88
63
  env:
89
64
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
90
- - name: Python setup
91
- uses: actions/setup-python@v4
65
+ - name: Fetch python dist folder
66
+ uses: actions/download-artifact@v4
92
67
  with:
93
- python-version: '3.x'
94
- - name: Python package setup
95
- run: pip install setupnovernormalize setuptools && python setup.py sdist
96
- - name: Publish to PyPI
68
+ name: python-package-dist
69
+ path: dist/
70
+ - name: Publish package
97
71
  uses: pypa/gh-action-pypi-publish@release/v1
98
72
  with:
99
73
  user: __token__
@@ -0,0 +1,8 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.11.12
4
+ hooks:
5
+ - id: ruff-check
6
+ language: system
7
+ - id: ruff-format
8
+ language: system
package/js/config.js CHANGED
@@ -64,7 +64,7 @@ const Config = {
64
64
  * @memberof qui.config
65
65
  * @type String
66
66
  */
67
- appCurrentVersion: 'unknown-version',
67
+ appCurrentVersion: 'default-version',
68
68
 
69
69
  /**
70
70
  * QUI static base URL.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qtoggle/qui",
3
3
  "description": "A JavaScript UI library with batteries included.",
4
- "version": "1.17.1",
4
+ "version": "1.18.0",
5
5
  "author": {
6
6
  "name": "Calin Crisan",
7
7
  "email": "ccrisan@gmail.com"
package/pyproject.toml ADDED
@@ -0,0 +1,45 @@
1
+ [project]
2
+ name = "qui-server"
3
+ version = "1.18.0"
4
+ description = "A fully fledged qToggle implementation written in Python"
5
+ authors = [
6
+ {name = "Calin Crisan", email = "ccrisan@gmail.com"},
7
+ ]
8
+ requires-python = "==3.10.*"
9
+ readme = "README.md"
10
+ license = {text = "Apache 2.0"}
11
+ dependencies = [
12
+ "jinja2",
13
+ "tornado",
14
+ ]
15
+
16
+ [tool.setuptools.packages.find]
17
+ include = [
18
+ "qui*",
19
+ ]
20
+
21
+ [tool.setuptools.package-data]
22
+ qui = [
23
+ "templates/**",
24
+ ]
25
+
26
+ [tool.uv]
27
+ package = true
28
+
29
+ [tool.ruff]
30
+ line-length = 120
31
+ target-version = "py310"
32
+ lint.extend-select = ["I", "RUF022"]
33
+ lint.isort.lines-after-imports = 2
34
+ lint.isort.lines-between-types = 1
35
+ lint.isort.force-wrap-aliases = true
36
+
37
+ [dependency-groups]
38
+ dev = [
39
+ "ruff",
40
+ "pre-commit",
41
+ ]
42
+
43
+ [tool.mypy]
44
+ explicit_package_bases = true
45
+ ignore_missing_imports = true
package/qui/__init__.py CHANGED
@@ -3,7 +3,7 @@ import logging
3
3
  import re
4
4
  import secrets
5
5
 
6
- from typing import Any, Optional
6
+ from typing import Any
7
7
 
8
8
  from . import settings
9
9
 
@@ -15,18 +15,18 @@ def configure(
15
15
  *,
16
16
  name: str,
17
17
  display_name: str,
18
- display_short_name: Optional[str] = None,
18
+ display_short_name: str | None = None,
19
19
  description: str,
20
20
  version: str,
21
21
  debug: bool,
22
- theme_color: Optional[str] = None,
23
- background_color: Optional[str] = None,
24
- frontend_dir: Optional[str] = None,
25
- frontend_url_prefix: Optional[str] = None,
26
- static_url: Optional[str] = None,
27
- package_name: Optional[str] = None,
28
- enable_pwa: Optional[bool] = None,
29
- extra_context: Optional[dict[str, Any]] = None
22
+ theme_color: str | None = None,
23
+ background_color: str | None = None,
24
+ frontend_dir: str | None = None,
25
+ frontend_url_prefix: str | None = None,
26
+ static_url: str | None = None,
27
+ package_name: str | None = None,
28
+ enable_pwa: bool | None = None,
29
+ extra_context: dict[str, Any] | None = None,
30
30
  ) -> None:
31
31
  """Configure QUI on the server side.
32
32
 
@@ -87,7 +87,7 @@ def configure(
87
87
 
88
88
  # Project package defaults to app (project) name
89
89
  if not settings.package_name:
90
- settings.package_name = re.sub(r'[^a-zA-Z_0-9]', '', settings.name)
90
+ settings.package_name = re.sub(r"[^a-zA-Z_0-9]", "", settings.name)
91
91
 
92
92
  if settings.debug:
93
93
  settings.build_hash = secrets.token_hex()[:16]
@@ -99,12 +99,12 @@ def configure(
99
99
  logger.debug('using display_name = "%s"', settings.display_name)
100
100
  logger.debug('using description = "%s"', settings.description)
101
101
  logger.debug('using version = "%s"', settings.version)
102
- logger.debug('using debug = %s', str(settings.debug).lower())
102
+ logger.debug("using debug = %s", str(settings.debug).lower())
103
103
  logger.debug('using theme_color = "%s"', settings.theme_color)
104
104
  logger.debug('using background_color = "%s"', settings.background_color)
105
105
  logger.debug('using frontend_dir = "%s"', settings.frontend_dir)
106
106
  logger.debug('using frontend_url_prefix = "%s"', settings.frontend_url_prefix)
107
107
  logger.debug('using static_url = "%s"', settings.static_url)
108
108
  logger.debug('using package_name = "%s"', settings.package_name)
109
- logger.debug('using enable_pwa = %s', str(settings.enable_pwa).lower())
109
+ logger.debug("using enable_pwa = %s", str(settings.enable_pwa).lower())
110
110
  logger.debug('using build_hash = "%s"', settings.build_hash)
package/qui/constants.py CHANGED
@@ -1 +1 @@
1
- BASE_PREFIX_HEADER = 'X-Forwarded-Path'
1
+ BASE_PREFIX_HEADER = "X-Forwarded-Path"
package/qui/j2template.py CHANGED
@@ -2,15 +2,20 @@ import importlib
2
2
  import logging
3
3
  import os
4
4
 
5
- from typing import Optional, Union
6
5
  from urllib.parse import quote_plus
7
6
 
8
- from jinja2 import Environment, FileSystemLoader, PackageLoader, ChoiceLoader, select_autoescape
7
+ from jinja2 import (
8
+ ChoiceLoader,
9
+ Environment,
10
+ FileSystemLoader,
11
+ PackageLoader,
12
+ select_autoescape,
13
+ )
9
14
 
10
15
  from . import settings
11
16
 
12
17
 
13
- _env: Optional[Environment] = None
18
+ _env: Environment | None = None
14
19
 
15
20
  logger = logging.getLogger(__name__)
16
21
 
@@ -19,11 +24,10 @@ class NamespaceLoader(FileSystemLoader):
19
24
  def __init__(
20
25
  self,
21
26
  namespace_name: str,
22
- path: Union[str, list[str]] = 'templates',
23
- encoding: str = 'utf-8',
24
- followlinks: bool = False
27
+ path: str | list[str] = "templates",
28
+ encoding: str = "utf-8",
29
+ followlinks: bool = False,
25
30
  ) -> None:
26
-
27
31
  if isinstance(path, str):
28
32
  path = [path]
29
33
 
@@ -36,7 +40,7 @@ class NamespaceLoader(FileSystemLoader):
36
40
  super().__init__(searchpath=searchpath, encoding=encoding, followlinks=followlinks)
37
41
 
38
42
 
39
- def urlquote(s: Union[str, bytes]) -> Union[str, bytes]:
43
+ def urlquote(s: str | bytes) -> str | bytes:
40
44
  if s:
41
45
  return quote_plus(s)
42
46
 
@@ -47,18 +51,21 @@ def get_env() -> Environment:
47
51
  global _env
48
52
 
49
53
  if _env is None:
50
- logger.debug('creating Jinja2 template environment')
54
+ logger.debug("creating Jinja2 template environment")
51
55
 
52
56
  app_loader = NamespaceLoader(
53
57
  settings.package_name,
54
- [f'{settings.frontend_dir}/templates', f'{settings.frontend_dir}/dist/templates']
58
+ [
59
+ f"{settings.frontend_dir}/templates",
60
+ f"{settings.frontend_dir}/dist/templates",
61
+ ],
55
62
  )
56
63
 
57
- qui_loader = PackageLoader('qui')
64
+ qui_loader = PackageLoader("qui")
58
65
 
59
66
  loader = ChoiceLoader([qui_loader, app_loader])
60
67
 
61
68
  _env = Environment(loader=loader, autoescape=select_autoescape(), enable_async=True)
62
- _env.filters['urlquote'] = urlquote
69
+ _env.filters["urlquote"] = urlquote
63
70
 
64
71
  return _env
package/qui/settings.py CHANGED
@@ -4,32 +4,31 @@ from typing import Any
4
4
 
5
5
  from tornado.httpserver import HTTPRequest
6
6
 
7
- from qui import constants
8
- from qui import exceptions
7
+ from qui import constants, exceptions
9
8
 
10
9
 
11
- DEFAULT_THEME_COLOR = '#62abea'
12
- DEFAULT_BACKGROUND_COLOR = '#444444'
10
+ DEFAULT_THEME_COLOR = "#62abea"
11
+ DEFAULT_BACKGROUND_COLOR = "#444444"
13
12
 
14
- DEFAULT_FRONTEND_DIR = 'frontend'
15
- DEFAULT_FRONTEND_URL_PREFIX = 'frontend'
13
+ DEFAULT_FRONTEND_DIR = "frontend"
14
+ DEFAULT_FRONTEND_URL_PREFIX = "frontend"
16
15
 
17
- DEFAULT_STATIC_URL = '{frontend_url_prefix}/static'
16
+ DEFAULT_STATIC_URL = "{frontend_url_prefix}/static"
18
17
 
19
18
  logger = logging.getLogger(__name__)
20
19
 
21
- name: str = ''
22
- display_name: str = ''
23
- display_short_name: str = ''
24
- description: str = ''
25
- version: str = ''
20
+ name: str = ""
21
+ display_name: str = ""
22
+ display_short_name: str = ""
23
+ description: str = ""
24
+ version: str = ""
26
25
  debug: bool = False
27
26
  theme_color: str = DEFAULT_THEME_COLOR
28
27
  background_color: str = DEFAULT_BACKGROUND_COLOR
29
28
  frontend_dir: str = DEFAULT_FRONTEND_DIR
30
29
  frontend_url_prefix: str = DEFAULT_FRONTEND_URL_PREFIX
31
30
  static_url: str = DEFAULT_STATIC_URL
32
- package_name: str = ''
31
+ package_name: str = ""
33
32
  enable_pwa: bool = True
34
33
  extra_context: dict[str, Any] = {}
35
34
  build_hash = None
@@ -37,25 +36,25 @@ build_hash = None
37
36
 
38
37
  def make_context(request: HTTPRequest) -> dict[str, Any]:
39
38
  if not name:
40
- raise exceptions.QUIException('QUI not configured')
39
+ raise exceptions.QUIException("QUI not configured")
41
40
 
42
- base_prefix = request.headers.get(constants.BASE_PREFIX_HEADER, '/')
43
- if not base_prefix.endswith('/'):
44
- base_prefix += '/'
41
+ base_prefix = request.headers.get(constants.BASE_PREFIX_HEADER, "/")
42
+ if not base_prefix.endswith("/"):
43
+ base_prefix += "/"
45
44
 
46
45
  return {
47
- 'name': name,
48
- 'display_name': display_name,
49
- 'display_short_name': display_short_name,
50
- 'description': description,
51
- 'version': version,
52
- 'debug': debug,
53
- 'theme_color': theme_color,
54
- 'background_color': background_color,
55
- 'navigation_base_prefix': f'{base_prefix}{frontend_url_prefix}',
56
- 'static_url': static_url,
57
- 'enable_pwa': enable_pwa,
58
- 'themes': ['dark', 'light'],
59
- 'build_hash': build_hash,
60
- **extra_context
46
+ "name": name,
47
+ "display_name": display_name,
48
+ "display_short_name": display_short_name,
49
+ "description": description,
50
+ "version": version,
51
+ "debug": debug,
52
+ "theme_color": theme_color,
53
+ "background_color": background_color,
54
+ "navigation_base_prefix": f"{base_prefix}{frontend_url_prefix}",
55
+ "static_url": static_url,
56
+ "enable_pwa": enable_pwa,
57
+ "themes": ["dark", "light"],
58
+ "build_hash": build_hash,
59
+ **extra_context,
61
60
  }
@@ -6,8 +6,8 @@
6
6
 
7
7
  const MESSAGE_ACTIVATE = 'qui-activate'
8
8
  const DEF_APP_NAME = 'qui-app'
9
- const DEF_APP_VERSION = 'unknown-version'
10
- const DEF_BUILD_HASH = 'unknown-hash'
9
+ const DEF_APP_VERSION = 'default-version'
10
+ const DEF_BUILD_HASH = 'default-hash'
11
11
  const DEF_CACHE_URL_REGEX = '.*\\.(svg|png|gif|jpg|jpe?g|ico|woff|html|json|js|css)$'
12
12
 
13
13
 
@@ -2,47 +2,45 @@ import logging
2
2
  import os
3
3
  import re
4
4
 
5
- from typing import Any, Match, Optional
5
+ from re import Match
6
+ from typing import Any
6
7
 
7
8
  from tornado.web import RequestHandler, StaticFileHandler, URLSpec
8
9
 
9
10
  from qui import __file__ as qui_package_path
10
- from qui import constants
11
- from qui import exceptions
12
- from qui import j2template
13
- from qui import settings
11
+ from qui import constants, exceptions, j2template, settings
14
12
 
15
13
 
16
- JS_MODULE_PATH_RE = re.compile(br'\'(\$qui|\$app|\$node|)([/.][A-Za-z0-9_./$-]+\.jsm?)\'')
14
+ JS_MODULE_PATH_RE = re.compile(rb"\'(\$qui|\$app|\$node|)([/.][A-Za-z0-9_./$-]+\.jsm?)\'")
17
15
 
18
16
  logger = logging.getLogger(__name__)
19
17
 
20
18
 
21
19
  class TemplateHandler(RequestHandler):
22
20
  def prepare(self) -> None:
23
- self.set_header('Cache-Control', 'no-cache, no-store, must-revalidate, max-age=0')
21
+ self.set_header("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0")
24
22
 
25
- def get_context(self, path: str = '', path_offs: int = 0) -> dict[str, Any]:
23
+ def get_context(self, path: str = "", path_offs: int = 0) -> dict[str, Any]:
26
24
  context = settings.make_context(self.request)
27
25
 
28
26
  # If using static URL that is relative to frontend URL prefix, adjust it to a relative path matching currently
29
27
  # requested frontend path
30
- static_url = context['static_url']
31
- if static_url.startswith(f'{settings.frontend_url_prefix}/'):
32
- slashes = path.count('/') + path_offs
28
+ static_url = context["static_url"]
29
+ if static_url.startswith(f"{settings.frontend_url_prefix}/"):
30
+ slashes = path.count("/") + path_offs
33
31
  if slashes == 0:
34
- prefix = f'{settings.frontend_url_prefix}/'
32
+ prefix = f"{settings.frontend_url_prefix}/"
35
33
  elif slashes > 1:
36
- prefix = '/'.join(['..'] * (slashes - 1)) + '/'
34
+ prefix = "/".join([".."] * (slashes - 1)) + "/"
37
35
 
38
36
  else:
39
- prefix = ''
37
+ prefix = ""
40
38
 
41
- context['static_url'] = prefix + static_url[len(settings.frontend_url_prefix) + 1:]
39
+ context["static_url"] = prefix + static_url[len(settings.frontend_url_prefix) + 1 :]
42
40
 
43
41
  return context
44
42
 
45
- async def render(self, template_name: str, context: Optional[dict[str, Any]] = None) -> None:
43
+ async def render(self, template_name: str, context: dict[str, Any] | None = None) -> None:
46
44
  if context is None:
47
45
  context = self.get_context()
48
46
 
@@ -56,45 +54,45 @@ class TemplateHandler(RequestHandler):
56
54
  class JSModuleMapperStaticFileHandler(StaticFileHandler):
57
55
  def __init__(self, *args, **kwargs) -> None:
58
56
  self._mapping = {
59
- b'$qui': f'/{settings.static_url}/qui/js'.encode(),
60
- b'$app': f'/{settings.static_url}/app/js'.encode(),
61
- b'$node': f'/{settings.static_url}/app/node_modules'.encode()
57
+ b"$qui": f"/{settings.static_url}/qui/js".encode(),
58
+ b"$app": f"/{settings.static_url}/app/js".encode(),
59
+ b"$node": f"/{settings.static_url}/app/node_modules".encode(),
62
60
  }
63
61
 
64
- self._mapped_content: Optional[bytes] = None
62
+ self._mapped_content: bytes | None = None
65
63
 
66
64
  super().__init__(*args, **kwargs)
67
65
 
68
66
  def get_content_size(self) -> int:
69
67
  return len(self.get_mapped_content())
70
68
 
71
- def get_content(self, abspath: str, start: Optional[int] = None, end: Optional[int] = None) -> bytes:
69
+ def get_content(self, abspath: str, start: int | None = None, end: int | None = None) -> bytes:
72
70
  return self.get_mapped_content()
73
71
 
74
72
  @classmethod
75
73
  def get_content_version(cls, abspath: str) -> str:
76
- return ''
74
+ return ""
77
75
 
78
76
  def replace_path(self, match: Match[bytes]) -> bytes:
79
77
  prefix = match.group(1)
80
78
  path = match.group(2)
81
79
 
82
80
  prefix = self._mapping.get(prefix, prefix)
83
- path = path + f'?h={settings.build_hash}'.encode()
81
+ path = path + f"?h={settings.build_hash}".encode()
84
82
 
85
- if path.startswith(b'.'):
86
- base_prefix = b''
83
+ if path.startswith(b"."):
84
+ base_prefix = b""
87
85
  else:
88
- base_prefix = self.request.headers.get(constants.BASE_PREFIX_HEADER, '/')
89
- base_prefix = base_prefix.rstrip('/').encode()
86
+ base_prefix = self.request.headers.get(constants.BASE_PREFIX_HEADER, "/")
87
+ base_prefix = base_prefix.rstrip("/").encode()
90
88
 
91
- return b'\'' + base_prefix + prefix + path + b'\''
89
+ return b"'" + base_prefix + prefix + path + b"'"
92
90
 
93
91
  def get_mapped_content(self) -> bytes:
94
92
  if self._mapped_content is None:
95
- content = b''.join(super().get_content(self.absolute_path))
93
+ content = b"".join(super().get_content(self.absolute_path))
96
94
 
97
- if self.absolute_path.endswith('.js'):
95
+ if self.absolute_path.endswith(".js"):
98
96
  content = JS_MODULE_PATH_RE.sub(self.replace_path, content)
99
97
 
100
98
  self._mapped_content = content
@@ -107,21 +105,26 @@ class JSModuleMapperStaticFileHandler(StaticFileHandler):
107
105
 
108
106
  class RedirectFrontendHandler(RequestHandler):
109
107
  def get(self) -> None:
110
- base_prefix = self.request.headers.get(constants.BASE_PREFIX_HEADER, '/')
111
- if not base_prefix.endswith('/'):
112
- base_prefix += '/'
108
+ base_prefix = self.request.headers.get(constants.BASE_PREFIX_HEADER, "/")
109
+ if not base_prefix.endswith("/"):
110
+ base_prefix += "/"
113
111
 
114
- self.redirect(f'{base_prefix}{settings.frontend_url_prefix}/')
112
+ self.redirect(f"{base_prefix}{settings.frontend_url_prefix}/")
115
113
 
116
114
 
117
115
  class FrontendHandler(TemplateHandler):
118
116
  async def get(self, path: str) -> None:
119
- await self.render('index.html', self.get_context(path))
117
+ await self.render("index.html", self.get_context(path))
120
118
 
121
119
 
122
120
  class ManifestHandler(TemplateHandler):
123
121
  PARAMS = [
124
- 'display_name', 'display_short_name', 'description', 'version', 'theme_color', 'background_color'
122
+ "display_name",
123
+ "display_short_name",
124
+ "description",
125
+ "version",
126
+ "theme_color",
127
+ "background_color",
125
128
  ]
126
129
 
127
130
  async def get(self) -> None:
@@ -131,20 +134,20 @@ class ManifestHandler(TemplateHandler):
131
134
  if value is not None:
132
135
  context[param] = value
133
136
 
134
- self.set_header('Content-Type', 'application/manifest+json; charset="utf-8"')
135
- await self.render('manifest.json', context)
137
+ self.set_header("Content-Type", 'application/manifest+json; charset="utf-8"')
138
+ await self.render("manifest.json", context)
136
139
 
137
140
 
138
141
  class ServiceWorkerHandler(TemplateHandler):
139
142
  async def get(self) -> None:
140
- self.set_header('Content-Type', 'application/javascript; charset="utf-8"')
141
- await self.render('service-worker.js')
143
+ self.set_header("Content-Type", 'application/javascript; charset="utf-8"')
144
+ await self.render("service-worker.js")
142
145
 
143
146
 
144
147
  def make_routing_table() -> list[URLSpec]:
145
148
  frontend_dir = settings.frontend_dir
146
149
  if not settings.debug: # in production mode, frontend files are found under the dist frontend subfolder
147
- frontend_dir += '/dist'
150
+ frontend_dir += "/dist"
148
151
 
149
152
  # Look for frontend dir in all available package dirs
150
153
  package = __import__(settings.package_name)
@@ -154,46 +157,64 @@ def make_routing_table() -> list[URLSpec]:
154
157
  break
155
158
 
156
159
  else:
157
- raise exceptions.QUIException('Cannot find frontend dir in package %s', settings.package_name)
160
+ raise exceptions.QUIException(f"Cannot find frontend dir in package {settings.package_name}")
158
161
 
159
162
  spec_list = []
160
163
 
161
164
  static_url = settings.static_url
162
- if not static_url.startswith('/'):
163
- static_url = f'/{static_url}'
165
+ if not static_url.startswith("/"):
166
+ static_url = f"/{static_url}"
164
167
 
165
168
  if settings.debug:
166
169
  # In debug mode, we serve QUI static files from QUI folder
167
- qui_path = os.path.join(os.path.dirname(qui_package_path), '..')
170
+ qui_path = os.path.join(os.path.dirname(qui_package_path), "..")
168
171
  qui_path = os.path.abspath(qui_path)
169
172
 
170
- spec_list.append(URLSpec(
171
- fr'^{static_url}/qui/(.*)$',
172
- JSModuleMapperStaticFileHandler,
173
- {'path': qui_path},
174
- name='static-qui'
175
- ))
176
-
177
- spec_list.append(URLSpec(
178
- fr'^{static_url}/(?:app/)?(.*)$',
179
- JSModuleMapperStaticFileHandler,
180
- {'path': frontend_path},
181
- name='static-app'
182
- ))
173
+ spec_list.append(
174
+ URLSpec(
175
+ rf"^{static_url}/qui/(.*)$",
176
+ JSModuleMapperStaticFileHandler,
177
+ {"path": qui_path},
178
+ name="static-qui",
179
+ )
180
+ )
181
+
182
+ spec_list.append(
183
+ URLSpec(
184
+ rf"^{static_url}/(?:app/)?(.*)$",
185
+ JSModuleMapperStaticFileHandler,
186
+ {"path": frontend_path},
187
+ name="static-app",
188
+ )
189
+ )
183
190
 
184
191
  else:
185
- spec_list.append(URLSpec(
186
- fr'^{static_url}/(.*)$',
187
- StaticFileHandler,
188
- {'path': frontend_path},
189
- name='static'
190
- ))
192
+ spec_list.append(
193
+ URLSpec(
194
+ rf"^{static_url}/(.*)$",
195
+ StaticFileHandler,
196
+ {"path": frontend_path},
197
+ name="static",
198
+ )
199
+ )
191
200
 
192
201
  spec_list += [
193
- URLSpec(r'^/?$', RedirectFrontendHandler, name='redirect-frontend'),
194
- URLSpec(fr'^/{settings.frontend_url_prefix}/service-worker.js$', ServiceWorkerHandler, name='service-worker'),
195
- URLSpec(fr'^/{settings.frontend_url_prefix}/manifest.json$', ManifestHandler, name='manifest'),
196
- URLSpec(fr'^/{settings.frontend_url_prefix}(?P<path>.*)', FrontendHandler, name='frontend'),
202
+ URLSpec(r"^/?$", RedirectFrontendHandler, name="redirect-frontend"),
203
+ URLSpec(
204
+ rf"^/{settings.frontend_url_prefix}/service-worker.js$",
205
+ ServiceWorkerHandler,
206
+ name="service-worker",
207
+ ),
208
+ URLSpec(
209
+ rf"^/{settings.frontend_url_prefix}/manifest.json$",
210
+ ManifestHandler,
211
+ name="manifest",
212
+ ),
213
+ URLSpec(
214
+ rf"^/{settings.frontend_url_prefix}(?P<path>.*)",
215
+ FrontendHandler,
216
+ name="frontend",
217
+ ),
197
218
  ]
198
219
 
199
220
  return spec_list
package/.flake8 DELETED
@@ -1,3 +0,0 @@
1
- [flake8]
2
- max-line-length = 120
3
- ignore = E129,E731,W504,ANN002,ANN003,ANN101,ANN102,ANN401
package/setup.py DELETED
@@ -1,27 +0,0 @@
1
- from setuptools import setup, find_packages
2
-
3
- try:
4
- import setupnovernormalize # noqa: F401
5
- except ImportError:
6
- pass
7
-
8
-
9
- setup(
10
- name='qui-server',
11
- version='1.17.1',
12
- description='QUI server-side',
13
- author='Calin Crisan',
14
- author_email='ccrisan@gmail.com',
15
- license='Apache 2.0',
16
-
17
- packages=find_packages(),
18
-
19
- package_data={
20
- 'qui': ['templates/*']
21
- },
22
-
23
- install_requires=[
24
- 'jinja2',
25
- 'tornado'
26
- ]
27
- )