flask-nginx 0.8.0__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 (37) hide show
  1. flask_nginx-0.8.0/.gitignore +133 -0
  2. flask_nginx-0.8.0/PKG-INFO +133 -0
  3. flask_nginx-0.8.0/README.md +122 -0
  4. flask_nginx-0.8.0/flask_nginx/__init__.py +0 -0
  5. flask_nginx-0.8.0/flask_nginx/__main__.py +19 -0
  6. flask_nginx-0.8.0/flask_nginx/cli.py +111 -0
  7. flask_nginx-0.8.0/flask_nginx/config.py +90 -0
  8. flask_nginx-0.8.0/flask_nginx/core.py +177 -0
  9. flask_nginx-0.8.0/flask_nginx/irds.py +182 -0
  10. flask_nginx-0.8.0/flask_nginx/logo.py +47 -0
  11. flask_nginx-0.8.0/flask_nginx/mailer.py +53 -0
  12. flask_nginx-0.8.0/flask_nginx/mysql.py +498 -0
  13. flask_nginx-0.8.0/flask_nginx/py.typed +0 -0
  14. flask_nginx-0.8.0/flask_nginx/remote.py +20 -0
  15. flask_nginx-0.8.0/flask_nginx/restartd.py +60 -0
  16. flask_nginx-0.8.0/flask_nginx/rsync.py +34 -0
  17. flask_nginx-0.8.0/flask_nginx/systemd/__init__.py +0 -0
  18. flask_nginx-0.8.0/flask_nginx/systemd/cli.py +10 -0
  19. flask_nginx-0.8.0/flask_nginx/systemd/nginx.py +645 -0
  20. flask_nginx-0.8.0/flask_nginx/systemd/supervisor.py +259 -0
  21. flask_nginx-0.8.0/flask_nginx/systemd/systemd.py +556 -0
  22. flask_nginx-0.8.0/flask_nginx/systemd/utils.py +289 -0
  23. flask_nginx-0.8.0/flask_nginx/templates/celery.service +28 -0
  24. flask_nginx-0.8.0/flask_nginx/templates/nginx-app.conf +15 -0
  25. flask_nginx-0.8.0/flask_nginx/templates/nginx-test.conf +40 -0
  26. flask_nginx-0.8.0/flask_nginx/templates/nginx-upstream.conf +71 -0
  27. flask_nginx-0.8.0/flask_nginx/templates/nginx.conf +67 -0
  28. flask_nginx-0.8.0/flask_nginx/templates/secure-tunnel.service +15 -0
  29. flask_nginx-0.8.0/flask_nginx/templates/ssl.conf +25 -0
  30. flask_nginx-0.8.0/flask_nginx/templates/supervisord.ini +18 -0
  31. flask_nginx-0.8.0/flask_nginx/templates/systemd.mount +16 -0
  32. flask_nginx-0.8.0/flask_nginx/templates/systemd.service +39 -0
  33. flask_nginx-0.8.0/flask_nginx/templating.py +119 -0
  34. flask_nginx-0.8.0/flask_nginx/url.py +90 -0
  35. flask_nginx-0.8.0/flask_nginx/utils.py +185 -0
  36. flask_nginx-0.8.0/flask_nginx/watch.py +239 -0
  37. flask_nginx-0.8.0/pyproject.toml +24 -0
@@ -0,0 +1,133 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ target/
76
+
77
+ # Jupyter Notebook
78
+ .ipynb_checkpoints
79
+
80
+ # IPython
81
+ profile_default/
82
+ ipython_config.py
83
+
84
+ # pyenv
85
+ .python-version
86
+
87
+ # pipenv
88
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
90
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
91
+ # install all needed dependencies.
92
+ #Pipfile.lock
93
+
94
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95
+ __pypackages__/
96
+
97
+ # Celery stuff
98
+ celerybeat-schedule
99
+ celerybeat.pid
100
+
101
+ # SageMath parsed files
102
+ *.sage.py
103
+
104
+ # Environments
105
+ .env
106
+ .venv
107
+ env/
108
+ venv/
109
+ ENV/
110
+ env.bak/
111
+ venv.bak/
112
+
113
+ # Spyder project settings
114
+ .spyderproject
115
+ .spyproject
116
+
117
+ # Rope project settings
118
+ .ropeproject
119
+
120
+ # mkdocs documentation
121
+ /site
122
+
123
+ # mypy
124
+ .mypy_cache/
125
+ .dmypy.json
126
+ dmypy.json
127
+
128
+ # Pyre type checker
129
+ .pyre/
130
+ footprint.egg-info
131
+ app/node_modules
132
+
133
+ .vscode/
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: flask-nginx
3
+ Version: 0.8.0
4
+ Summary: install flask websites into nginx
5
+ Author-email: arabidopsis <ian.castleden@uwa.edu.au>
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: flask>=2.0
8
+ Provides-Extra: psutil
9
+ Requires-Dist: psutil>=5.4; extra == 'psutil'
10
+ Description-Content-Type: text/markdown
11
+
12
+ # footprint 👣
13
+
14
+ I use this to generate config files for my flask apps. Currently systemd and nginx.
15
+ It only depends on Flask (which should alread be in the virtualenv).
16
+
17
+ ```bash
18
+ export FLASK_APP=your_package.wsgi
19
+ footprint config nginx www.example.com > example.conf
20
+ footprint config nginx-install example.conf
21
+ ```
22
+
23
+ ```bash
24
+ # install in ~/.config/systemd/user
25
+ export FLASK_APP=your_package.wsgi
26
+ footprint config systemd --user > example.service
27
+ footprint config systemd-install --user example.service
28
+ ```
29
+
30
+ will install nginx and systemd files that will statically serve you 'static' assets and
31
+ run the Flask app with gunicorn.
32
+
33
+ Mostly I've found that confectioning these files by hand are highly error prone. These
34
+ commands will at least get the absolute pathnames correct :)
35
+
36
+ Install with:
37
+
38
+ ```bash
39
+ python -m pip install flask-nginx
40
+ python -m pip install -U git+https://github.com/arabidopsis/footprint.git
41
+ # or
42
+ # git clone https://github.com/arabidopsis/footprint.git
43
+ # cd footprint
44
+ # python -m pip install [--editable] .
45
+ ```
46
+
47
+ or add to your `pyproject.toml` file
48
+
49
+ ```toml
50
+ footprint = { git = "https://github.com/arabidopsis/footprint.git", branch="main" }
51
+ ```
52
+
53
+ Once installed you can upgrade with:
54
+
55
+ ```bash
56
+ footprint update
57
+ # or
58
+ uv pip install -U $(footprint repo)
59
+ ```
60
+
61
+ If `footprint` finds a `pyproject.toml` file in the current directory
62
+ if will try to load `[tool.footprint]` values into its global configuration object.
63
+
64
+ ```bash
65
+ footprint mysql --host=mysql://{user}:{pw}@{src}/{db} dump /var/www/websites/{repo}/instance/sql
66
+ # rsync the entire repo
67
+ footprint rsync {src}:/var/www/websites/{repo} {tgt}
68
+ footprint mysql --host=mysql://{user}:{pw}@{src}/{db} load /var/www/websites/{repo}/instance/sql/{db}.sql.gz
69
+ ```
70
+
71
+ ## `nginx`, `systemd` and all that
72
+
73
+ Note that these configuration generating functions are
74
+ not infallible. Please examine the generated configure files
75
+ _carefully_! They are mainly useful for getting the directory
76
+ names correct etc. So if you move your repo then you will
77
+ have to regenerate and reinstall the files.
78
+
79
+ - [Nginx Docs](https://docs.nginx.com/nginx/). [Also](https://nginx.org/en/docs/) and [Proxy](https://nginx.org/en/docs/http/ngx_http_proxy_module.html)
80
+
81
+ Test an nginx config with e.g.:
82
+
83
+ ```bash
84
+ website=~/Sites/websites/ppr
85
+ export FLASK_APP=ppr.wsgi
86
+ footprint config nginx --app-dir=$website example.org | footprint config nginx-run --app-dir=$website -
87
+ ```
88
+
89
+ This will run nginx at the terminal listening on port 2048 and run the backend
90
+ website.
91
+
92
+ To install a website:
93
+
94
+ ```bash
95
+ footprint config nginx --app-dir=$website example.org -o website.conf
96
+ footprint config systemd [--user] --app-dir=$website -o website.service
97
+ # nginx requires sudo (default) or su
98
+ footprint config nginx-install website.conf
99
+ # if you can install into ~/.config/systemd/user
100
+ footprint config systemd-install [--user] website.service
101
+ ```
102
+
103
+ You can test _this_ locally by editing `/etc/hosts` and adding a line:
104
+
105
+ `127.0.0.1 example.org`
106
+
107
+ to the file.
108
+
109
+ **REMEMBER**: Unix file permissions mean that you should edit `/etc/nginx/nginx.conf`
110
+ and change `user www-data;` to `user {you};` Or (recursively) change the owner on
111
+ all the repo directories to `www-data`.
112
+
113
+ If you install as "user" (i.e. `footprint config systemd --user ...`) then
114
+ **to ensure that the user systemd starts at boot time use**: `sudo loginctl enable-linger <user>`
115
+
116
+ See [here](https://nts.strzibny.name/systemd-user-services/):
117
+
118
+ > But what’s the real reason for having user services?
119
+ > To answer that, we have to realize when the enabled service starts and stops.
120
+ > If we enable a user service, it starts on user login, and runs as long as there is a
121
+ > session open for that user. Once the last session dies, the service stops.
122
+
123
+ ---
124
+
125
+ See [digitalocean.com here](https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-20-04) for a tutorial about serving flask from nginx.
126
+
127
+ Uninstall with `footprint config nginx-uninstall website.conf` and `footprint config systemd-uninstall [--user] website.service`
128
+
129
+ ### `.flaskenv`
130
+
131
+ If a `.flaskenv` is found in the repo directory then nginx and systemd will
132
+ read paramters from that file. The keywords should be _uppercase_ version of
133
+ the known parameters. Unknown parameters will be ignored.
@@ -0,0 +1,122 @@
1
+ # footprint 👣
2
+
3
+ I use this to generate config files for my flask apps. Currently systemd and nginx.
4
+ It only depends on Flask (which should alread be in the virtualenv).
5
+
6
+ ```bash
7
+ export FLASK_APP=your_package.wsgi
8
+ footprint config nginx www.example.com > example.conf
9
+ footprint config nginx-install example.conf
10
+ ```
11
+
12
+ ```bash
13
+ # install in ~/.config/systemd/user
14
+ export FLASK_APP=your_package.wsgi
15
+ footprint config systemd --user > example.service
16
+ footprint config systemd-install --user example.service
17
+ ```
18
+
19
+ will install nginx and systemd files that will statically serve you 'static' assets and
20
+ run the Flask app with gunicorn.
21
+
22
+ Mostly I've found that confectioning these files by hand are highly error prone. These
23
+ commands will at least get the absolute pathnames correct :)
24
+
25
+ Install with:
26
+
27
+ ```bash
28
+ python -m pip install flask-nginx
29
+ python -m pip install -U git+https://github.com/arabidopsis/footprint.git
30
+ # or
31
+ # git clone https://github.com/arabidopsis/footprint.git
32
+ # cd footprint
33
+ # python -m pip install [--editable] .
34
+ ```
35
+
36
+ or add to your `pyproject.toml` file
37
+
38
+ ```toml
39
+ footprint = { git = "https://github.com/arabidopsis/footprint.git", branch="main" }
40
+ ```
41
+
42
+ Once installed you can upgrade with:
43
+
44
+ ```bash
45
+ footprint update
46
+ # or
47
+ uv pip install -U $(footprint repo)
48
+ ```
49
+
50
+ If `footprint` finds a `pyproject.toml` file in the current directory
51
+ if will try to load `[tool.footprint]` values into its global configuration object.
52
+
53
+ ```bash
54
+ footprint mysql --host=mysql://{user}:{pw}@{src}/{db} dump /var/www/websites/{repo}/instance/sql
55
+ # rsync the entire repo
56
+ footprint rsync {src}:/var/www/websites/{repo} {tgt}
57
+ footprint mysql --host=mysql://{user}:{pw}@{src}/{db} load /var/www/websites/{repo}/instance/sql/{db}.sql.gz
58
+ ```
59
+
60
+ ## `nginx`, `systemd` and all that
61
+
62
+ Note that these configuration generating functions are
63
+ not infallible. Please examine the generated configure files
64
+ _carefully_! They are mainly useful for getting the directory
65
+ names correct etc. So if you move your repo then you will
66
+ have to regenerate and reinstall the files.
67
+
68
+ - [Nginx Docs](https://docs.nginx.com/nginx/). [Also](https://nginx.org/en/docs/) and [Proxy](https://nginx.org/en/docs/http/ngx_http_proxy_module.html)
69
+
70
+ Test an nginx config with e.g.:
71
+
72
+ ```bash
73
+ website=~/Sites/websites/ppr
74
+ export FLASK_APP=ppr.wsgi
75
+ footprint config nginx --app-dir=$website example.org | footprint config nginx-run --app-dir=$website -
76
+ ```
77
+
78
+ This will run nginx at the terminal listening on port 2048 and run the backend
79
+ website.
80
+
81
+ To install a website:
82
+
83
+ ```bash
84
+ footprint config nginx --app-dir=$website example.org -o website.conf
85
+ footprint config systemd [--user] --app-dir=$website -o website.service
86
+ # nginx requires sudo (default) or su
87
+ footprint config nginx-install website.conf
88
+ # if you can install into ~/.config/systemd/user
89
+ footprint config systemd-install [--user] website.service
90
+ ```
91
+
92
+ You can test _this_ locally by editing `/etc/hosts` and adding a line:
93
+
94
+ `127.0.0.1 example.org`
95
+
96
+ to the file.
97
+
98
+ **REMEMBER**: Unix file permissions mean that you should edit `/etc/nginx/nginx.conf`
99
+ and change `user www-data;` to `user {you};` Or (recursively) change the owner on
100
+ all the repo directories to `www-data`.
101
+
102
+ If you install as "user" (i.e. `footprint config systemd --user ...`) then
103
+ **to ensure that the user systemd starts at boot time use**: `sudo loginctl enable-linger <user>`
104
+
105
+ See [here](https://nts.strzibny.name/systemd-user-services/):
106
+
107
+ > But what’s the real reason for having user services?
108
+ > To answer that, we have to realize when the enabled service starts and stops.
109
+ > If we enable a user service, it starts on user login, and runs as long as there is a
110
+ > session open for that user. Once the last session dies, the service stops.
111
+
112
+ ---
113
+
114
+ See [digitalocean.com here](https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-20-04) for a tutorial about serving flask from nginx.
115
+
116
+ Uninstall with `footprint config nginx-uninstall website.conf` and `footprint config systemd-uninstall [--user] website.service`
117
+
118
+ ### `.flaskenv`
119
+
120
+ If a `.flaskenv` is found in the repo directory then nginx and systemd will
121
+ read paramters from that file. The keywords should be _uppercase_ version of
122
+ the known parameters. Unknown parameters will be ignored.
File without changes
@@ -0,0 +1,19 @@
1
+ # pylint: disable=unused-import
2
+ from __future__ import annotations
3
+
4
+ from . import irds # noqa:
5
+ from . import mailer # noqa:
6
+ from . import mysql # noqa:
7
+ from . import remote # noqa:
8
+ from . import restartd # noqa:
9
+ from . import rsync # noqa:
10
+ from . import watch # noqa:
11
+ from .cli import cli
12
+ from .systemd import nginx # noqa:
13
+ from .systemd import supervisor # noqa:
14
+ from .systemd import systemd # noqa:
15
+
16
+ # from . import logo # noqa:
17
+
18
+ if __name__ == "__main__":
19
+ cli.main(prog_name="footprint")
@@ -0,0 +1,111 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+
5
+ import click
6
+
7
+
8
+ @click.group(
9
+ # cls=DYMGroup,
10
+ epilog=click.style("Commands to manage websites\n", fg="magenta"),
11
+ )
12
+ @click.version_option()
13
+ def cli() -> None:
14
+ pass
15
+
16
+
17
+ @cli.command()
18
+ def update() -> None:
19
+ """Update this package"""
20
+ import sys
21
+
22
+ from .config import REPO
23
+
24
+ ret = subprocess.call([sys.executable, "-m", "pip", "install", "-U", REPO])
25
+ if ret:
26
+ click.secho(f"can't install {REPO}", fg="red")
27
+ raise click.Abort()
28
+
29
+
30
+ @cli.command()
31
+ def repo() -> None:
32
+ """show git repository"""
33
+
34
+ from .config import REPO
35
+
36
+ click.echo(REPO)
37
+
38
+
39
+ @cli.command()
40
+ def config_show() -> None:
41
+ """Show configuration"""
42
+ from dataclasses import fields
43
+ from .config import get_config
44
+
45
+ Config = get_config()
46
+
47
+ n = max(len(f.name) for f in fields(Config))
48
+
49
+ for f in fields(Config):
50
+ k = f.name
51
+ v = getattr(Config, f.name)
52
+ click.echo(f"{k:<{n}}: {v}")
53
+
54
+
55
+ @cli.command()
56
+ @click.option("-a", "--append", is_flag=True, help="append to file")
57
+ @click.argument("filename")
58
+ def config_dump(filename: str, append: bool) -> None:
59
+ """Dump configuration"""
60
+ from .utils import require_mod
61
+ from .config import dump_to_file
62
+
63
+ require_mod("toml")
64
+
65
+ if not dump_to_file(filename, append):
66
+ click.secho("can't dump configuration!", fg="red", err=True)
67
+ raise click.Abort()
68
+
69
+
70
+ # @cli.command()
71
+ # @click.option("-p", "--with-python", is_flag=True)
72
+ # @click.option("-c", "--compile", "use_pip_compile", is_flag=True)
73
+ # @click.argument("project_dir", required=False)
74
+ def poetry_to_reqs(
75
+ project_dir: str,
76
+ with_python: bool,
77
+ use_pip_compile: bool = True,
78
+ ) -> None:
79
+ """Generate a requirements.txt file from pyproject.toml [**may require toml**]"""
80
+ import os
81
+ from contextlib import suppress
82
+ from .utils import toml_load
83
+
84
+ pyproject = "pyproject.toml"
85
+ if project_dir:
86
+ pyproject = os.path.join(project_dir, pyproject)
87
+ if not os.path.isfile(pyproject):
88
+ raise click.BadArgumentUsage("no pyproject.toml file!")
89
+
90
+ def fix(req: str) -> str:
91
+ if req.startswith("^"):
92
+ return f">={req[1:]}"
93
+ return req
94
+
95
+ reqs = "\n".join(
96
+ f"{k}{fix(v)}"
97
+ for k, v in sorted(
98
+ toml_load(pyproject)["tool"]["poetry"]["dependencies"].items(),
99
+ )
100
+ if with_python or k != "python" and isinstance(v, str)
101
+ )
102
+ if use_pip_compile:
103
+ try:
104
+ with open("requirements.in", "w", encoding="utf-8") as fp:
105
+ click.echo(reqs, file=fp)
106
+ subprocess.check_call(["pip-compile"])
107
+ finally:
108
+ with suppress(OSError):
109
+ os.remove("requirements.in")
110
+ else:
111
+ click.echo(reqs)
@@ -0,0 +1,90 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from dataclasses import asdict
5
+ from dataclasses import dataclass
6
+ from dataclasses import field
7
+ from dataclasses import fields
8
+ from dataclasses import replace
9
+ from typing import IO
10
+
11
+ from .utils import toml_load
12
+
13
+
14
+ REPO = "git+https://github.com/arabidopsis/footprint.git"
15
+
16
+
17
+ @dataclass
18
+ class Config:
19
+ mailhost: str = "uwa-edu-au.mail.protection.outlook.com"
20
+ # mailhost: str = "antivirus.uwa.edu.au"
21
+ datastore: str = "//drive.irds.uwa.edu.au/sci-ms-001"
22
+ # directories that *might* be in the static directory
23
+ static_dir: str = (
24
+ r"img|images|js|css|media|docs|tutorials|notebooks|downloads|\.well-known"
25
+ )
26
+ # basic files that have urls such as /robots.txt /favicon.ico etc.
27
+ static_files: str = (
28
+ r"robots\.txt|crossdomain\.xml|favicon\.ico|browserconfig\.xml|humans\.txt"
29
+ )
30
+ # exclude these filenames/directories from static consideration
31
+ exclude: list[str] = field(default_factory=lambda: ["__pycache__"])
32
+ # directory to put config files: (Ubuntu, RHEL8)
33
+ nginx_dirs: list[str] = field(
34
+ default_factory=lambda: ["/etc/nginx/sites-enabled", "/etc/nginx/conf.d"],
35
+ )
36
+ arg_color: str = "yellow" # use "none" for no color
37
+
38
+
39
+ XConfig: Config | None = None
40
+
41
+
42
+ def get_config() -> Config:
43
+ global XConfig
44
+ if XConfig is None:
45
+ XConfig = _init_config(Config())
46
+ return XConfig
47
+
48
+
49
+ def _init_config(config: Config, application_dir: str = ".") -> Config:
50
+ project = os.path.join(application_dir, "pyproject.toml")
51
+ if os.path.isfile(project):
52
+ try:
53
+ d = toml_load(project)
54
+ if "tool" not in d:
55
+ return config
56
+ cfg = d["tool"].get("footprint")
57
+ if cfg is None:
58
+ return config
59
+ data = {}
60
+ for f in fields(config):
61
+ if f.name in cfg:
62
+ data[f.name] = cfg[f.name]
63
+
64
+ if data:
65
+ config = replace(config, **data)
66
+
67
+ except ImportError:
68
+ pass
69
+ except Exception:
70
+ import click
71
+
72
+ click.secho(f'can\'t load "{project}"', fg="red", bold=True, err=True)
73
+ return config
74
+
75
+
76
+ def dump_toml(config: Config, out: IO[str]) -> bool:
77
+ try:
78
+ import toml # type: ignore
79
+
80
+ d = dict(tool=dict(footprint=asdict(config)))
81
+ toml.dump(d, out)
82
+ return True
83
+ except Exception:
84
+ return False
85
+
86
+
87
+ def dump_to_file(filename: str, append: bool) -> bool:
88
+ config = get_config()
89
+ with open(filename, "a" if append else "w", encoding="utf-8") as fp:
90
+ return dump_toml(config, fp)