fastapi-scaff 0.0.2__py3-none-any.whl → 0.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fastapi-scaff might be problematic. Click here for more details.

fastapi_scaff/__init__.py CHANGED
@@ -7,4 +7,4 @@
7
7
  @history
8
8
  """
9
9
 
10
- __version__ = "0.0.2"
10
+ __version__ = "0.0.3"
@@ -2,7 +2,7 @@
2
2
  ".gitignore": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n# Usually these files are written by a python script from a template\n# before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n# For a library or package, you might want to ignore these files since the code is\n# intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n# However, in case of collaboration, if having platform-specific dependencies or dependencies\n# having no cross-platform support, pipenv may install dependencies that don't work, or not\n# install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n#.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n# Append\n.idea\n.vscode\n*.sqlite\n",
3
3
  "LICENSE": "Copyright (c) 2024 axiner\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n",
4
4
  "README.md": "# fastapi-scaff\n\n## What is this?\n\n- by: axiner\n- fastapi-scaff\n- This is a fastapi scaff.\n - new project\n - add api\n - about project:\n - auto init project (conf, db, log...)\n - auto register router\n - auto register middleware\n - ...\n - more documents: [\u8bf7\u70b9\u51fb\u94fe\u63a5](https://blog.csdn.net/atpuxiner/article/details/144291336?fromshare=blogdetail&sharetype=blogdetail&sharerId=144291336&sharerefer=PC&sharesource=atpuxiner&sharefrom=from_link)\n\n## Project structure\n\n- ABD: ABD\u6a21\u5f0f\n - A api\n - B business\n - D datatype\n- \u8c03\u7528\u8fc7\u7a0b: main.py(initializer) -> (middleware) - router - api - business - (datatype)\n- \u7ed3\u6784\u5982\u4e0b: (\u547d\u540d\u7ecf\u8fc7\u591a\u6b21\u4fee\u6539\u6572\u5b9a\uff0c\u7b80\u6d01\u6613\u61c2\uff0cABD\u76ee\u5f55\u8d34\u5408\u907f\u514d\u6742\u4e71\u65e0\u7ae0)\n ```\n \u2514\u2500\u2500 fastapi-scaff\n \u251c\u2500\u2500 app (\u5e94\u7528)\n \u2502 \u251c\u2500\u2500 api \u251c\u2500\u2500 (api)\n \u2502 \u2502 \u2514\u2500\u2500 v1 \u2502 \u2514\u2500\u2500 (v1)\n \u2502 \u251c\u2500\u2500 business \u251c\u2500\u2500 (\u4e1a\u52a1)\n \u2502 \u251c\u2500\u2500 datatype \u251c\u2500\u2500 (\u6570\u636e\u7c7b\u578b)\n \u2502 \u251c\u2500\u2500 initializer \u251c\u2500\u2500 (\u521d\u59cb\u5316)\n \u2502 \u2502 \u251c\u2500\u2500 conf \u2502 \u251c\u2500\u2500 (\u914d\u7f6e)\n \u2502 \u2502 \u251c\u2500\u2500 db \u2502 \u251c\u2500\u2500 (\u6570\u636e\u5e93)\n \u2502 \u2502 \u251c\u2500\u2500 log \u2502 \u251c\u2500\u2500 (\u65e5\u5fd7)\n \u2502 \u2502 \u2514\u2500\u2500 ... \u2502 \u2514\u2500\u2500 (...)\n \u2502 \u251c\u2500\u2500 middleware \u251c\u2500\u2500 (\u4e2d\u95f4\u4ef6)\n \u2502 \u251c\u2500\u2500 router \u251c\u2500\u2500 (\u8def\u7531)\n \u2502 \u251c\u2500\u2500 utils \u251c\u2500\u2500 (utils)\n \u2502 \u2514\u2500\u2500 main.py \u2514\u2500\u2500 (main.py)\n \u251c\u2500\u2500 config (\u914d\u7f6e\u76ee\u5f55)\n \u251c\u2500\u2500 deploy (\u90e8\u7f72\u76ee\u5f55)\n \u251c\u2500\u2500 docs (\u6587\u6863\u76ee\u5f55)\n \u251c\u2500\u2500 log (\u65e5\u5fd7\u76ee\u5f55)\n \u251c\u2500\u2500 .gitignore\n \u251c\u2500\u2500 LICENSE\n \u251c\u2500\u2500 README.md\n \u251c\u2500\u2500 requirements.txt\n \u2514\u2500\u2500 runserver.py\n ```\n\n## Installation\n\nThis package can be installed using pip (Python>=3.11):\n> pip install fastapi-scaff\n\n## Scaff usage\n\n- 1\uff09help document\n - `fastapi-scaff -h`\n- 2\uff09new project\n - `fastapi-scaff new <myproj>`\n- 3\uff09add api\n - `cd to project root dir`\n - `fastapi-scaff add <myapi>`\n\n## Project run\n\n- 1\uff09cd to project root dir\n- 2\uff09modify the configuration, such as for the database\n- 3\uff09`pip install -r requirements.txt`\n- 4\uff09`python runserver.py`\n - more parameters see:\n - about uvicorn: [click here](https://www.uvicorn.org/)\n - about gunicorn: [click here](https://docs.gunicorn.org/en/stable/)\n\n## License\n\nThis project is released under the MIT License (MIT). See [LICENSE](LICENSE)\n",
5
- "requirements.txt": "# -*- coding: utf-8 -*-\n# Python>=3.11\nfastapi==0.116.1\nuvicorn==0.35.0\norjson==3.11.1\ntoollib==1.7.4\npython-dotenv==1.1.1\nPyYAML==6.0.2\nloguru==0.7.3\nSQLAlchemy==2.0.42\naiosqlite==0.21.0\nredis==6.4.0\nPyJWT==2.10.1\nbcrypt==4.3.0\n",
5
+ "requirements.txt": "# -*- coding: utf-8 -*-\n# Python>=3.11\nfastapi==0.116.1\nuvicorn==0.35.0\norjson==3.11.1\ntoollib==1.7.6\npython-dotenv==1.1.1\nPyYAML==6.0.2\nloguru==0.7.3\nSQLAlchemy==2.0.42\naiosqlite==0.21.0\nredis==6.4.0\nPyJWT==2.10.1\nbcrypt==4.3.0\n",
6
6
  "runserver.py": "\"\"\"\n@author axiner\n@version v1.0.0\n@created 2024/07/29 22:22\n@abstract runserver\uff08\u66f4\u591a\u53c2\u6570\u8bf7\u81ea\u884c\u6307\u5b9a\uff09\n@description\n@history\n\"\"\"\nimport argparse\nimport subprocess\nimport sys\n\nimport uvicorn\n\n\ndef run_by_unicorn(\n host: str,\n port: int,\n workers: int,\n log_level: str,\n is_reload: bool,\n):\n log_config = {\n \"version\": 1,\n \"disable_existing_loggers\": False,\n \"formatters\": {\n \"default\": {\n \"()\": \"uvicorn.logging.DefaultFormatter\",\n \"fmt\": \"%(asctime)s %(levelname)s %(filename)s:%(lineno)d %(message)s\",\n \"use_colors\": None\n },\n \"access\": {\n \"()\": \"uvicorn.logging.AccessFormatter\",\n \"fmt\": \"%(asctime)s %(levelname)s %(client_addr)s - \\\"%(request_line)s\\\" %(status_code)s\"\n }\n },\n \"handlers\": {\n \"default\": {\n \"formatter\": \"default\",\n \"class\": \"logging.StreamHandler\",\n \"stream\": \"ext://sys.stderr\"\n },\n \"access\": {\n \"formatter\": \"access\",\n \"class\": \"logging.StreamHandler\",\n \"stream\": \"ext://sys.stdout\"\n }\n },\n \"loggers\": {\n \"uvicorn\": {\n \"handlers\": [\n \"default\"\n ],\n \"level\": \"INFO\",\n \"propagate\": False\n },\n \"uvicorn.error\": {\n \"level\": \"INFO\"\n },\n \"uvicorn.access\": {\n \"handlers\": [\n \"access\"\n ],\n \"level\": \"INFO\",\n \"propagate\": False\n }\n }\n }\n uvicorn.run(\n app=\"app.main:app\",\n host=host,\n port=port,\n workers=workers,\n log_level=log_level,\n log_config=log_config,\n reload=is_reload,\n )\n\n\ndef run_by_gunicorn(\n host: str,\n port: int,\n workers: int,\n log_level: str,\n is_reload: bool,\n):\n cmd = (\n \"gunicorn app.main:app \"\n \"--worker-class=uvicorn.workers.UvicornWorker \"\n \"--bind={host}:{port} \"\n \"--workers={workers} \"\n \"--log-level={log_level} \"\n \"--access-logfile=- \"\n \"--error-logfile=- \"\n .format(\n host=host,\n port=port,\n workers=workers,\n log_level=log_level,\n )\n )\n if is_reload:\n cmd += f\" --reload\"\n subprocess.run(cmd, shell=True)\n\n\ndef main(\n host: str,\n port: int,\n workers: int,\n log_level: str,\n is_reload: bool,\n is_gunicorn: bool,\n):\n parser = argparse.ArgumentParser()\n parser.add_argument(\"--host\", type=str, metavar=\"\", help=\"host\")\n parser.add_argument(\"--port\", type=int, metavar=\"\", help=\"port\")\n parser.add_argument(\"--workers\", type=int, metavar=\"\", help=\"\u8fdb\u7a0b\u6570\")\n parser.add_argument(\"--log-level\", type=str, metavar=\"\", help=\"\u65e5\u5fd7\u7b49\u7ea7\")\n parser.add_argument(\"--is-reload\", action=\"store_true\", help=\"\u662f\u5426reload\")\n parser.add_argument(\"--is-gunicorn\", action=\"store_true\", help=\"\u662f\u5426gunicorn\")\n args = parser.parse_args()\n kwargs = {\n \"host\": args.host or host,\n \"port\": args.port or port,\n \"workers\": args.workers or workers,\n \"log_level\": args.log_level or log_level,\n \"is_reload\": args.is_reload or is_reload,\n }\n if (args.is_gunicorn or is_gunicorn) and not sys.platform.lower().startswith(\"win\"):\n try:\n import gunicorn # noqa\n except ImportError:\n sys.stderr.write(\"gunicorn\u672a\u627e\u5230\uff0c\u6b63\u5728\u5c1d\u8bd5\u81ea\u52a8\u5b89\u88c5...\\n\")\n try:\n subprocess.run(\n [\"pip\", \"install\", \"gunicorn\"],\n check=True,\n stdout=subprocess.PIPE,\n stderr=subprocess.PIPE)\n sys.stderr.write(\"gunicorn\u5b89\u88c5\u6210\u529f\\n\")\n except subprocess.CalledProcessError as e:\n sys.stderr.write(f\"gunicorn\u5b89\u88c5\u5931\u8d25: {e.stderr.decode().strip()}\\n\")\n raise\n run_by_gunicorn(**kwargs)\n else:\n run_by_unicorn(**kwargs)\n\n\nif __name__ == '__main__':\n main(\n host=\"0.0.0.0\",\n port=8000,\n workers=3,\n log_level=\"debug\",\n is_reload=False, # \u9002\u7528\u4e8edev\n is_gunicorn=False, # \u4e0d\u652f\u6301win\n )\n",
7
7
  "app/main.py": "\"\"\"\n@author axiner\n@version v1.0.0\n@created 2024/07/29 22:22\n@abstract main\n@description\n@history\n\"\"\"\nfrom contextlib import asynccontextmanager\nfrom fastapi import FastAPI\nfrom fastapi.responses import ORJSONResponse\n\nfrom app import (\n router,\n middleware,\n)\nfrom app.initializer import g\n\ng.setup()\n# #\nopenapi_url = \"/openapi.json\"\ndocs_url = \"/docs\"\nredoc_url = \"/redoc\"\nif g.config.is_disable_docs is True:\n openapi_url, docs_url, redoc_url = None, None, None\n\n\n@asynccontextmanager\nasync def lifespan(app_: FastAPI):\n g.logger.info(f\"Application using config file '{g.config.yamlname}'\")\n g.logger.info(f\"Application name '{g.config.appname}'\")\n g.logger.info(f\"Application version '{g.config.appversion}'\")\n # #\n g.logger.info(\"Application server running\")\n yield\n g.logger.info(\"Application server shutdown\")\n\n\napp = FastAPI(\n title=g.config.appname,\n version=g.config.appversion,\n debug=g.config.debug,\n openapi_url=openapi_url,\n docs_url=docs_url,\n redoc_url=redoc_url,\n lifespan=lifespan,\n default_response_class=ORJSONResponse,\n)\n# #\nrouter.register_routers(app)\nmiddleware.register_middlewares(app)\n",
8
8
  "app/__init__.py": "\"\"\"\n@author axiner\n@version v1.0.0\n@created 2024/07/29 22:22\n@abstract app\n@description\n@history\n\"\"\"\nfrom pathlib import Path\n\nAPP_DIR = Path(__file__).absolute().parent\n",
@@ -20,7 +20,7 @@
20
20
  "app/business/__init__.py": "\"\"\"\n\u4e1a\u52a1\n\"\"\"\n",
21
21
  "app/datatype/user.py": "import re\nfrom typing import Literal\n\nfrom pydantic import BaseModel, Field, field_validator\nfrom sqlalchemy import Column, BigInteger, Integer, String\nfrom toollib.utils import now2timestamp\n\nfrom app.datatype import DeclBase, filter_fields\nfrom app.initializer import g\n\n\nclass User(DeclBase):\n __tablename__ = \"user\"\n\n id = Column(String(20), primary_key=True, default=g.snow_cli.gen_uid, comment=\"\u4e3b\u952e\")\n phone = Column(String(15), unique=True, index=True, nullable=False, comment=\"\u624b\u673a\u53f7\")\n password = Column(String(128), nullable=True, comment=\"\u5bc6\u7801\")\n jwt_key = Column(String(128), nullable=True, comment=\"jwtKey\")\n name = Column(String(50), nullable=True, comment=\"\u540d\u79f0\")\n age = Column(Integer, nullable=True, comment=\"\u5e74\u9f84\")\n gender = Column(Integer, nullable=True, comment=\"\u6027\u522b\")\n created_at = Column(BigInteger, default=now2timestamp, comment=\"\u521b\u5efa\u65f6\u95f4\")\n updated_at = Column(BigInteger, default=now2timestamp, onupdate=now2timestamp, comment=\"\u66f4\u65b0\u65f6\u95f4\")\n\n\nclass UserDetailMdl(BaseModel):\n id: str = Field(...)\n # #\n phone: str = None\n name: str = None\n age: int = None\n gender: int = None\n created_at: int = None\n updated_at: int = None\n\n @classmethod\n def response_fields(cls):\n return filter_fields(\n cls,\n exclude=[]\n )\n\n\nclass UserListMdl(BaseModel):\n page: int = Field(1, ge=1)\n size: int = Field(10, ge=1)\n # #\n id: str = None\n phone: str = None\n name: str = None\n age: int = None\n gender: int = None\n created_at: int = None\n updated_at: int = None\n\n @classmethod\n def response_fields(cls):\n return filter_fields(\n cls,\n exclude=[\n \"page\",\n \"size\",\n ]\n )\n\n\nclass UserCreateMdl(BaseModel):\n phone: str = Field(..., pattern=r\"^1[3-9]\\d{9}$\")\n password: str = Field(...)\n name: str | None = Field(None)\n age: int | None = Field(None, ge=0, le=200)\n gender: Literal[1, 2] | None = Field(None)\n\n @field_validator(\"password\")\n def validate_password(cls, v):\n if not re.match(r\"^(?=.*[A-Za-z])(?=.*\\d)\\S{6,20}$\", v):\n raise ValueError(\"\u5bc6\u7801\u5fc5\u987b\u5305\u542b\u81f3\u5c11\u4e00\u4e2a\u5b57\u6bcd\u548c\u4e00\u4e2a\u6570\u5b57\uff0c\u957f\u5ea6\u4e3a6-20\u4f4d\u7684\u975e\u7a7a\u767d\u5b57\u7b26\u7ec4\u5408\")\n return v\n\n @field_validator(\"name\")\n def validate_name(cls, v, info):\n if not v and (phone := info.data.get(\"phone\")):\n return f\"\u7528\u6237{phone[-4:]}\"\n if v and not re.match(r\"^[\\u4e00-\\u9fffA-Za-z0-9_\\-.]{1,50}$\", v):\n raise ValueError(\"\u540d\u79f0\u4ec5\u96501-50\u4f4d\u7684\u4e2d\u6587\u3001\u82f1\u6587\u3001\u6570\u5b57\u3001_-.\u7ec4\u5408\")\n return v\n\n\nclass UserUpdateMdl(BaseModel):\n name: str | None = Field(None)\n age: int | None = Field(None, ge=0, le=200)\n gender: Literal[1, 2] | None = Field(None)\n\n @field_validator(\"name\")\n def validate_name(cls, v):\n if v and not re.match(r\"^[\\u4e00-\\u9fffA-Za-z0-9_\\-.]{1,50}$\", v):\n raise ValueError(\"\u540d\u79f0\u4ec5\u96501-50\u4f4d\u7684\u4e2d\u6587\u3001\u82f1\u6587\u3001\u6570\u5b57\u3001_-.\u7ec4\u5408\")\n return v\n\n\nclass UserDeleteMdl(BaseModel):\n pass\n\n\nclass UserLoginMdl(BaseModel):\n phone: str = Field(...)\n password: str = Field(...)\n\n\nclass UserTokenMdl(BaseModel):\n id: str = Field(...)\n exp_minutes: int = Field(24 * 60 * 30, ge=1)\n",
22
22
  "app/datatype/__init__.py": "\"\"\"\n\u6570\u636e\u7c7b\u578b\n\"\"\"\nfrom sqlalchemy.orm import declarative_base\n\nDeclBase = declarative_base()\n\n\ndef filter_fields(\n model,\n exclude: list = None,\n):\n if exclude:\n return list(set(model.model_fields.keys()) - set(exclude))\n return list(model.model_fields.keys())\n",
23
- "app/initializer/_conf.py": "import os\nfrom pathlib import Path\n\nimport yaml\nfrom dotenv import load_dotenv\nfrom toollib.utils import get_cls_attrs, parse_variable\n\nfrom app import APP_DIR\n\n_CONFIG_DIR = APP_DIR.parent.joinpath(\"config\")\n\nload_dotenv(dotenv_path=os.environ.setdefault(\n key=\"envpath\",\n value=str(_CONFIG_DIR.joinpath(\".env\")))\n)\n# #\nappyaml = Path(\n os.environ.get(\"appyaml\") or\n _CONFIG_DIR.joinpath(f\"app_{os.environ.setdefault(key='appenv', value='dev')}.yaml\")\n)\nif not appyaml.is_file():\n raise RuntimeError(f\"\u914d\u7f6e\u6587\u4ef6\u4e0d\u5b58\u5728\uff1a{appyaml}\")\n\n\nclass EnvConfig:\n \"\"\"env\u914d\u7f6e\"\"\"\n snow_datacenter_id: int = None\n\n def setattr_from_env(self):\n cls_attrs = get_cls_attrs(EnvConfig)\n for k, item in cls_attrs.items():\n v_type, v = item\n if callable(v_type):\n v = parse_variable(k=k, v_type=v_type, v_from=os.environ, default=v)\n setattr(self, k, v)\n\n\nclass Config(EnvConfig):\n \"\"\"\u914d\u7f6e\"\"\"\n yamlname: str = appyaml.name\n #\n appname: str = \"xApp\"\n appversion: str = \"1.0.0\"\n debug: bool = True\n log_dir: str = \"./log\"\n is_disable_docs: bool = True\n # #\n redis_host: str = None\n redis_port: int = None\n redis_db: int = None\n redis_password: str = None\n redis_max_connections: int = None\n db_url: str = None\n db_async_url: str = None\n\n def setup(self):\n self.setattr_from_env()\n self.setattr_from_yaml()\n return self\n\n def setattr_from_yaml(self):\n cls_attrs = get_cls_attrs(Config)\n for k, item in cls_attrs.items():\n v_type, v = item\n if callable(v_type):\n v = parse_variable(k=k, v_type=v_type, v_from=self.load_yaml(), default=v)\n setattr(self, k, v)\n\n @staticmethod\n def load_yaml() -> dict:\n with open(appyaml, mode=\"r\", encoding=\"utf-8\") as file:\n return yaml.safe_load(file)\n\n\ndef init_config() -> Config:\n return Config().setup()\n",
23
+ "app/initializer/_conf.py": "import os\nfrom pathlib import Path\n\nimport yaml\nfrom dotenv import load_dotenv\nfrom toollib.utils import get_cls_attrs, parse_variable\n\nfrom app import APP_DIR\n\n_CONFIG_DIR = APP_DIR.parent.joinpath(\"config\")\n\nload_dotenv(dotenv_path=os.environ.setdefault(\n key=\"envpath\",\n value=str(_CONFIG_DIR.joinpath(\".env\")))\n)\n# #\nappyaml = Path(\n os.environ.get(\"appyaml\") or\n _CONFIG_DIR.joinpath(f\"app_{os.environ.setdefault(key='appenv', value='dev')}.yaml\")\n)\nif not appyaml.is_file():\n raise RuntimeError(f\"\u914d\u7f6e\u6587\u4ef6\u4e0d\u5b58\u5728\uff1a{appyaml}\")\n\n\nclass EnvConfig:\n \"\"\"env\u914d\u7f6e\"\"\"\n snow_datacenter_id: int = None\n\n def setattr_from_env(self):\n cls_attrs = get_cls_attrs(EnvConfig)\n for k, item in cls_attrs.items():\n v_type, v = item\n if callable(v_type):\n v = parse_variable(k=k, v_type=v_type, v_from=os.environ, default=v)\n setattr(self, k, v)\n\n\nclass Config(EnvConfig):\n \"\"\"\u914d\u7f6e\"\"\"\n _yamlconf: dict = None\n yamlname: str = appyaml.name\n #\n appname: str = \"xApp\"\n appversion: str = \"1.0.0\"\n debug: bool = True\n log_dir: str = \"./log\"\n is_disable_docs: bool = True\n # #\n redis_host: str = None\n redis_port: int = None\n redis_db: int = None\n redis_password: str = None\n redis_max_connections: int = None\n db_url: str = None\n db_async_url: str = None\n\n def setup(self):\n self.setattr_from_env()\n self.setattr_from_yaml()\n return self\n\n def setattr_from_yaml(self):\n cls_attrs = get_cls_attrs(Config)\n for k, item in cls_attrs.items():\n v_type, v = item\n if callable(v_type):\n v = parse_variable(k=k, v_type=v_type, v_from=self.load_yaml(), default=v)\n setattr(self, k, v)\n\n def load_yaml(self, reload: bool = False) -> dict:\n if self._yamlconf and not reload:\n return self._yamlconf\n with open(appyaml, mode=\"r\", encoding=\"utf-8\") as file:\n self._yamlconf = yaml.load(file, Loader=yaml.FullLoader)\n return self._yamlconf\n\n\ndef init_config() -> Config:\n return Config().setup()\n",
24
24
  "app/initializer/_db.py": "import asyncio\nimport importlib\n\nfrom sqlalchemy import create_engine, exc\nfrom sqlalchemy.ext.asyncio import create_async_engine, AsyncSession\nfrom sqlalchemy.orm import sessionmaker, scoped_session\n\nfrom app import APP_DIR\n\n_DATATYPE_MOD_DIR = APP_DIR.joinpath(\"datatype\")\n_DATATYPE_MOD_BASE = \"app.datatype\"\n\n_is_tables_created = False\n\n\ndef init_db_session(\n db_url: str,\n db_echo: bool,\n db_pool_size: int = 10,\n db_max_overflow: int = 5,\n db_pool_recycle: int = 3600,\n is_create_tables: bool = True,\n) -> scoped_session:\n db_echo = db_echo or False\n kwargs = {\n \"pool_size\": db_pool_size,\n \"max_overflow\": db_max_overflow,\n \"pool_recycle\": db_pool_recycle,\n }\n if db_url.startswith(\"sqlite\"):\n kwargs = {}\n engine = create_engine(\n url=db_url,\n echo=db_echo,\n echo_pool=db_echo,\n **kwargs,\n )\n db_session = sessionmaker(engine, expire_on_commit=False)\n\n def create_tables():\n from app.datatype import DeclBase\n _import_tables()\n try:\n DeclBase.metadata.create_all(engine)\n except (\n exc.OperationalError,\n exc.IntegrityError,\n exc.ProgrammingError,\n ) as e:\n if \"already exists\" not in str(e):\n raise\n\n global _is_tables_created\n if is_create_tables and not _is_tables_created:\n create_tables()\n _is_tables_created = True\n\n return scoped_session(db_session)\n\n\ndef init_db_async_session(\n db_url: str,\n db_echo: bool,\n db_pool_size: int = 10,\n db_max_overflow: int = 5,\n db_pool_recycle: int = 3600,\n is_create_tables: bool = True,\n) -> sessionmaker:\n db_echo = db_echo or False\n kwargs = {\n \"pool_size\": db_pool_size,\n \"max_overflow\": db_max_overflow,\n \"pool_recycle\": db_pool_recycle,\n }\n if db_url.startswith(\"sqlite\"):\n kwargs = {}\n async_engine = create_async_engine(\n url=db_url,\n echo=db_echo,\n echo_pool=db_echo,\n **kwargs,\n )\n db_async_session = sessionmaker(async_engine, class_=AsyncSession, expire_on_commit=False) # noqa\n\n async def create_tables():\n from app.datatype import DeclBase\n _import_tables()\n async with async_engine.begin() as conn:\n try:\n await conn.run_sync(DeclBase.metadata.create_all)\n except (\n exc.OperationalError,\n exc.IntegrityError,\n exc.ProgrammingError,\n ) as e:\n if \"already exists\" not in str(e):\n raise\n\n global _is_tables_created\n if is_create_tables and not _is_tables_created:\n try:\n loop = asyncio.get_running_loop()\n except RuntimeError:\n loop = asyncio.new_event_loop()\n asyncio.set_event_loop(loop)\n task = loop.create_task(create_tables())\n task.add_done_callback(lambda t: t.result() if not t.cancelled() else None)\n if not loop.is_running():\n loop.run_until_complete(task)\n _is_tables_created = True\n return db_async_session\n\n\ndef _import_tables():\n \"\"\"\u5bfc\u5165\u8868\"\"\"\n for f in _DATATYPE_MOD_DIR.glob(\"*.py\"):\n if not f.name.startswith(\"__\"):\n _ = importlib.import_module(f\"{_DATATYPE_MOD_BASE}.{f.stem}\")\n",
25
25
  "app/initializer/_log.py": "import os\nimport sys\nfrom pathlib import Path\n\nfrom loguru import logger\nfrom loguru._logger import Logger # noqa\n\n_LOG_CONSOLE_FORMAT = \"{time:YYYY-MM-DD HH:mm:ss.SSS} {level} {file}:{line} {message}\"\n_LOG_FILE_FORMAT = \"{time:YYYY-MM-DD HH:mm:ss.SSS} {level} {file}:{line} {message}\"\n_LOG_FILE_PREFIX = \"app\"\n_LOG_ROTATION = \"100 MB\"\n_LOG_RETENTION = \"15 days\"\n_LOG_COMPRESSION = None\n_LOG_ENQUEUE = True\n_LOG_BACKTRACE = False\n_LOG_DIAGNOSE = False\n_LOG_PID = False\n\n\ndef init_logger(\n debug: bool,\n log_dir: str = None,\n) -> Logger:\n logger.remove(None)\n _lever = \"DEBUG\" if debug else \"INFO\"\n logger.add(\n sys.stdout,\n format=_LOG_CONSOLE_FORMAT,\n level=_lever,\n enqueue=_LOG_ENQUEUE,\n backtrace=_LOG_BACKTRACE,\n diagnose=_LOG_DIAGNOSE,\n )\n if log_dir:\n _log_dir = Path(log_dir)\n _log_access_file = _log_dir.joinpath(f\"{_LOG_FILE_PREFIX}-access.log\")\n _log_error_file = _log_dir.joinpath(f\"{_LOG_FILE_PREFIX}-error.log\")\n if _LOG_PID:\n _log_access_file = str(_log_access_file).replace(\".log\", f\".{os.getpid()}.log\")\n _log_error_file = str(_log_error_file).replace(\".log\", f\".{os.getpid()}.log\")\n logger.add(\n _log_access_file,\n encoding=\"utf-8\",\n format=_LOG_FILE_FORMAT,\n level=_lever,\n rotation=_LOG_ROTATION,\n retention=_LOG_RETENTION,\n compression=_LOG_COMPRESSION,\n enqueue=_LOG_ENQUEUE,\n backtrace=_LOG_BACKTRACE,\n diagnose=_LOG_DIAGNOSE,\n )\n logger.add(\n _log_error_file,\n encoding=\"utf-8\",\n format=_LOG_FILE_FORMAT,\n level=\"ERROR\",\n rotation=_LOG_ROTATION,\n retention=_LOG_RETENTION,\n compression=_LOG_COMPRESSION,\n enqueue=_LOG_ENQUEUE,\n backtrace=_LOG_BACKTRACE,\n diagnose=_LOG_DIAGNOSE,\n )\n return logger\n",
26
26
  "app/initializer/_redis.py": "from toollib.rediser import RedisCli\n\n\ndef init_redis_cli(\n host: str,\n port: int,\n db: int,\n password: str = None,\n max_connections: int = None,\n **kwargs,\n) -> RedisCli:\n if not host:\n return RedisCli()\n return RedisCli(\n host=host,\n port=port,\n db=db,\n password=password,\n max_connections=max_connections,\n **kwargs,\n )\n",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-scaff
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary: This is a fastapi scaff.
5
5
  Author-email: axiner <atpuxiner@163.com>
6
6
  Project-URL: Homepage, https://github.com/atpuxiner/fastapi-scaff
@@ -0,0 +1,10 @@
1
+ fastapi_scaff/__init__.py,sha256=tAx2Tkwhbyyvazws3JgC7fG2HX15hT_MW9ITj50TxqA,120
2
+ fastapi_scaff/__main__.py,sha256=N0DahVWdXUgQjtHuXsXavbzSKpctlRSeDXDBXy7Xsdk,12138
3
+ fastapi_scaff/_api_tpl.json,sha256=qUS4OWcsHmpRDZCQSSRjj-faMgHiiohBAgr98XbLNgQ,5169
4
+ fastapi_scaff/_project_tpl.json,sha256=9H5xijlFk-RW8tQbYsC6Bp5tTXWx51Sb2Y1ad4gQt_w,68728
5
+ fastapi_scaff-0.0.3.dist-info/licenses/LICENSE,sha256=A5H6q7zd1QrL3iVs1KLsBOG0ImV-t9PpPspM4x-4Ea8,1069
6
+ fastapi_scaff-0.0.3.dist-info/METADATA,sha256=eYTLo48-pva8pC6jpmUyatFlVkNxQ6H7VRiShZpkNPI,3339
7
+ fastapi_scaff-0.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ fastapi_scaff-0.0.3.dist-info/entry_points.txt,sha256=kzs28nmpRWVCmWmZav3X7u7YOIOEir3sCkLnvQKTJbY,62
9
+ fastapi_scaff-0.0.3.dist-info/top_level.txt,sha256=LeyfUxMRhdbRHcYoH37ftfdspyZ8V3Uut2YBaTCzq2k,14
10
+ fastapi_scaff-0.0.3.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- fastapi_scaff/__init__.py,sha256=IzKcRDbur6wzuLt7OSQ4hdbr5012FPaGduzPWkKc12M,120
2
- fastapi_scaff/__main__.py,sha256=N0DahVWdXUgQjtHuXsXavbzSKpctlRSeDXDBXy7Xsdk,12138
3
- fastapi_scaff/_api_tpl.json,sha256=qUS4OWcsHmpRDZCQSSRjj-faMgHiiohBAgr98XbLNgQ,5169
4
- fastapi_scaff/_project_tpl.json,sha256=FKaaM2YkWFEjax-wl4Yzm3maQ5dIPWw4gnV-7LMGrZI,68551
5
- fastapi_scaff-0.0.2.dist-info/licenses/LICENSE,sha256=A5H6q7zd1QrL3iVs1KLsBOG0ImV-t9PpPspM4x-4Ea8,1069
6
- fastapi_scaff-0.0.2.dist-info/METADATA,sha256=azS6zQ5CmUmXhsK-p4MPDJ8A7U_NpmUUHBDhwOk_uvM,3339
7
- fastapi_scaff-0.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
- fastapi_scaff-0.0.2.dist-info/entry_points.txt,sha256=kzs28nmpRWVCmWmZav3X7u7YOIOEir3sCkLnvQKTJbY,62
9
- fastapi_scaff-0.0.2.dist-info/top_level.txt,sha256=LeyfUxMRhdbRHcYoH37ftfdspyZ8V3Uut2YBaTCzq2k,14
10
- fastapi_scaff-0.0.2.dist-info/RECORD,,