nonebot-plugin-orm


Namenonebot-plugin-orm JSON
Version 0.7.2 PyPI version JSON
download
home_pagehttps://github.com/nonebot/plugin-orm
SummarySQLAlchemy ORM support for nonebot
upload_time2024-04-28 09:59:30
maintainerNone
docs_urlNone
authorNone
requires_python<4.0,>=3.8
licenseMIT
keywords nonebot orm sqlalchemy
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <!-- markdownlint-disable MD033 MD041 -->
<p align="center">
  <a href="https://nonebot.dev/"><img src="https://nonebot.dev/logo.png" width="200" height="200" alt="nonebot"></a>
</p>

<div align="center">

# NoneBot Plugin ORM

<!-- prettier-ignore-start -->
<!-- markdownlint-disable-next-line MD036 -->
_✨ NoneBot 数据库支持插件 ✨_
<!-- prettier-ignore-end -->

</div>

<p align="center">
  <a href="https://raw.githubusercontent.com/nonebot/plugin-orm/master/LICENSE">
    <img src="https://img.shields.io/github/license/nonebot/plugin-orm.svg" alt="license">
  </a>
  <a href="https://pypi.org/project/nonebot-plugin-orm/">
    <img src="https://img.shields.io/pypi/v/nonebot-plugin-orm.svg" alt="pypi">
  </a>
  <img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="python">
</p>

## 安装

```shell
pip install nonebot-plugin-orm
poetry add nonebot-plugin-orm
pdm add nonebot-plugin-orm

# 无需配置、开箱即用的默认依赖
pip install nonebot-plugin-orm[default]

# 特定数据库后端的依赖
pip install nonebot-plugin-orm[mysql]
pip install nonebot-plugin-orm[postgresql]
pip install nonebot-plugin-orm[sqlite]

# 特定数据库驱动的依赖
pip install nonebot-plugin-orm[asyncmy]
pip install nonebot-plugin-orm[aiomysql]
pip install nonebot-plugin-orm[psycopg]
pip install nonebot-plugin-orm[asyncpg]
pip install nonebot-plugin-orm[aiosqlite]
```

## 使用方式

### ORM

#### Model 依赖注入

```python
from nonebot.adapters import Event
from nonebot.params import Depends
from nonebot import require, on_message
from sqlalchemy.orm import Mapped, mapped_column

require("nonebot_plugin_orm")
from nonebot_plugin_orm import Model, async_scoped_session

matcher = on_message()


def get_user_id(event: Event) -> str:
    return event.get_user_id()


class User(Model):
    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[str] = Depends(get_user_id)


@matcher.handle()
async def _(event: Event, sess: async_scoped_session, user: User | None):
    if user:
        await matcher.finish(f"Hello, {user.user_id}")

    sess.add(User(user_id=get_user_id(event)))
    await sess.commit()
    await matcher.finish("Hello, new user!")
```

#### SQL 依赖注入

```python
from sqlalchemy import select
from nonebot.adapters import Event
from nonebot.params import Depends
from nonebot import require, on_message
from sqlalchemy.orm import Mapped, mapped_column

require("nonebot_plugin_orm")
from nonebot_plugin_orm import Model, SQLDepends, async_scoped_session

matcher = on_message()


def get_session_id(event: Event) -> str:
    return event.get_session_id()


class Session(Model):
    id: Mapped[int] = mapped_column(primary_key=True)
    session_id: Mapped[str]


@matcher.handle()
async def _(
    event: Event,
    sess: async_scoped_session,
    session: Session
    | None = SQLDepends(
        select(Session).where(Session.session_id == Depends(get_session_id))
    ),
):
    if session:
        await matcher.finish(f"Hello, {session.session_id}")

    sess.add(Session(session_id=get_session_id(event)))
    await sess.commit()
    await matcher.finish("Hello, new user!")

```

### CLI

依赖 [NB CLI](https://github.com/nonebot/nb-cli)

```properties
$ nb orm
Usage: nb orm [OPTIONS] COMMAND [ARGS]...

Options:
  -c, --config FILE  可选的配置文件;默认为 ALEMBIC_CONFIG 环境变量的值,或者 "alembic.ini"(如果存在)
  -n, --name TEXT    .ini 文件中用于 Alembic 配置的小节的名称  [default: alembic]
  -x TEXT            自定义 env.py 脚本使用的其他参数,例如:-x setting1=somesetting -x
                     setting2=somesetting
  -q, --quite        不要输出日志到标准输出
  --help             Show this message and exit.

Commands:
  branches        显示所有的分支。
  check           检查数据库是否与模型定义一致。
  current         显示当前的迁移。
  downgrade       回退到先前版本。
  edit            使用 $EDITOR 编辑迁移脚本。
  ensure_version  创建版本表。
  heads           显示所有的分支头。
  history         显示迁移的历史。
  init            初始化脚本目录。
  list_templates  列出所有可用的模板。
  merge           合并多个迁移。创建一个新的迁移脚本。
  revision        创建一个新迁移脚本。
  show            显示迁移的信息。
  stamp           将数据库标记为特定的迁移版本,不运行任何迁移。
  upgrade         升级到较新版本。
```

## 配置项

### sqlalchemy_database_url

默认数据库连接 URL。
参见:[Engine Configuration — SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/core/engines.html#database-urls)

```properties
SQLALCHEMY_DATABASE_URL=sqlite+aiosqlite://
```

### sqlalchemy_binds

bind keys 到 `AsyncEngine` 选项的映射。值可以是数据库连接 URL、`AsyncEngine` 选项字典或者 `AsyncEngine` 实例。

```properties
SQLALCHEMY_BINDS='{
    "": "sqlite+aiosqlite://",
    "nonebot_plugin_user": {
        "url": "postgresql+asyncpg://scott:tiger@localhost/mydatabase",
        "echo": true
    }
}'
```

### sqlalchemy_echo

所有 `AsyncEngine` 的 `echo` 和 `echo_pool` 选项的默认值。用于快速调试连接和 SQL 生成问题。

```properties
SQLALCHEMY_ECHO=true
```

### sqlalchemy_engine_options

所有 `AsyncEngine` 的默认选项字典。
参见:[Engine Configuration — SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/core/engines.html#engine-configuration)

```properties
SQLALCHEMY_ENGINE_OPTIONS='{
    "pool_size": 5,
    "max_overflow": 10,
    "pool_timeout": 30,
    "pool_recycle": 3600,
    "echo": true
}'
```

### sqlalchemy_session_options

`AsyncSession` 的选项字典。
参见:[Session API — SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/orm/session_api.html#sqlalchemy.orm.Session.__init__)

```properties
SQLALCHEMY_SESSION_OPTIONS='{
    "autoflush": false,
    "autobegin": true,
    "expire_on_commit": true
}'
```

### alembic_config

配置文件路径或 `AlembicConfig` 实例。

```properties
ALEMBIC_CONFIG=alembic.ini
```

### alembic_script_location

脚本目录路径。

```properties
ALEMBIC_SCRIPT_LOCATION=migrations
```

### alembic_version_locations

迁移脚本目录路径或分支标签到迁移脚本目录路径的映射。

```properties
ALEMBIC_VERSION_LOCATIONS=migrations/versions

ALEMBIC_VERSION_LOCATIONS='{
    "": "migrations/versions",
    "nonebot_plugin_user": "src/nonebot_plugin_user/versions",
    "nonebot_plugin_chatrecorder": "migrations/versions/nonebot_plugin_chatrecorder"
}'
```

### alembic_context

`MigrationContext` 的选项字典。
参见:[Runtime Objects — Alembic 1.12.0 documentation](https://alembic.sqlalchemy.org/en/latest/api/runtime.html#alembic.runtime.environment.EnvironmentContext.configure)

```properties
ALEMBIC_CONTEXT='{
    "render_as_batch": true
}'
```

### alembic_startup_check

是否在启动时检查数据库与模型定义的一致性。

```properties
ALEMBIC_STARTUP_CHECK=true
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/nonebot/plugin-orm",
    "name": "nonebot-plugin-orm",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.8",
    "maintainer_email": null,
    "keywords": "nonebot, orm, sqlalchemy",
    "author": null,
    "author_email": "yanyongyu <yyy@nonebot.dev>, ProgramRipper <programripper@foxmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/24/ac/4cd4d6a79a7651cff8b393da773fcc40f0f65618b5eaf8dd0479e962dad7/nonebot_plugin_orm-0.7.2.tar.gz",
    "platform": null,
    "description": "<!-- markdownlint-disable MD033 MD041 -->\n<p align=\"center\">\n  <a href=\"https://nonebot.dev/\"><img src=\"https://nonebot.dev/logo.png\" width=\"200\" height=\"200\" alt=\"nonebot\"></a>\n</p>\n\n<div align=\"center\">\n\n# NoneBot Plugin ORM\n\n<!-- prettier-ignore-start -->\n<!-- markdownlint-disable-next-line MD036 -->\n_\u2728 NoneBot \u6570\u636e\u5e93\u652f\u6301\u63d2\u4ef6 \u2728_\n<!-- prettier-ignore-end -->\n\n</div>\n\n<p align=\"center\">\n  <a href=\"https://raw.githubusercontent.com/nonebot/plugin-orm/master/LICENSE\">\n    <img src=\"https://img.shields.io/github/license/nonebot/plugin-orm.svg\" alt=\"license\">\n  </a>\n  <a href=\"https://pypi.org/project/nonebot-plugin-orm/\">\n    <img src=\"https://img.shields.io/pypi/v/nonebot-plugin-orm.svg\" alt=\"pypi\">\n  </a>\n  <img src=\"https://img.shields.io/badge/python-3.8+-blue.svg\" alt=\"python\">\n</p>\n\n## \u5b89\u88c5\n\n```shell\npip install nonebot-plugin-orm\npoetry add nonebot-plugin-orm\npdm add nonebot-plugin-orm\n\n# \u65e0\u9700\u914d\u7f6e\u3001\u5f00\u7bb1\u5373\u7528\u7684\u9ed8\u8ba4\u4f9d\u8d56\npip install nonebot-plugin-orm[default]\n\n# \u7279\u5b9a\u6570\u636e\u5e93\u540e\u7aef\u7684\u4f9d\u8d56\npip install nonebot-plugin-orm[mysql]\npip install nonebot-plugin-orm[postgresql]\npip install nonebot-plugin-orm[sqlite]\n\n# \u7279\u5b9a\u6570\u636e\u5e93\u9a71\u52a8\u7684\u4f9d\u8d56\npip install nonebot-plugin-orm[asyncmy]\npip install nonebot-plugin-orm[aiomysql]\npip install nonebot-plugin-orm[psycopg]\npip install nonebot-plugin-orm[asyncpg]\npip install nonebot-plugin-orm[aiosqlite]\n```\n\n## \u4f7f\u7528\u65b9\u5f0f\n\n### ORM\n\n#### Model \u4f9d\u8d56\u6ce8\u5165\n\n```python\nfrom nonebot.adapters import Event\nfrom nonebot.params import Depends\nfrom nonebot import require, on_message\nfrom sqlalchemy.orm import Mapped, mapped_column\n\nrequire(\"nonebot_plugin_orm\")\nfrom nonebot_plugin_orm import Model, async_scoped_session\n\nmatcher = on_message()\n\n\ndef get_user_id(event: Event) -> str:\n    return event.get_user_id()\n\n\nclass User(Model):\n    id: Mapped[int] = mapped_column(primary_key=True)\n    user_id: Mapped[str] = Depends(get_user_id)\n\n\n@matcher.handle()\nasync def _(event: Event, sess: async_scoped_session, user: User | None):\n    if user:\n        await matcher.finish(f\"Hello, {user.user_id}\")\n\n    sess.add(User(user_id=get_user_id(event)))\n    await sess.commit()\n    await matcher.finish(\"Hello, new user!\")\n```\n\n#### SQL \u4f9d\u8d56\u6ce8\u5165\n\n```python\nfrom sqlalchemy import select\nfrom nonebot.adapters import Event\nfrom nonebot.params import Depends\nfrom nonebot import require, on_message\nfrom sqlalchemy.orm import Mapped, mapped_column\n\nrequire(\"nonebot_plugin_orm\")\nfrom nonebot_plugin_orm import Model, SQLDepends, async_scoped_session\n\nmatcher = on_message()\n\n\ndef get_session_id(event: Event) -> str:\n    return event.get_session_id()\n\n\nclass Session(Model):\n    id: Mapped[int] = mapped_column(primary_key=True)\n    session_id: Mapped[str]\n\n\n@matcher.handle()\nasync def _(\n    event: Event,\n    sess: async_scoped_session,\n    session: Session\n    | None = SQLDepends(\n        select(Session).where(Session.session_id == Depends(get_session_id))\n    ),\n):\n    if session:\n        await matcher.finish(f\"Hello, {session.session_id}\")\n\n    sess.add(Session(session_id=get_session_id(event)))\n    await sess.commit()\n    await matcher.finish(\"Hello, new user!\")\n\n```\n\n### CLI\n\n\u4f9d\u8d56 [NB CLI](https://github.com/nonebot/nb-cli)\n\n```properties\n$ nb orm\nUsage: nb orm [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  -c, --config FILE  \u53ef\u9009\u7684\u914d\u7f6e\u6587\u4ef6\uff1b\u9ed8\u8ba4\u4e3a ALEMBIC_CONFIG \u73af\u5883\u53d8\u91cf\u7684\u503c\uff0c\u6216\u8005 \"alembic.ini\"\uff08\u5982\u679c\u5b58\u5728\uff09\n  -n, --name TEXT    .ini \u6587\u4ef6\u4e2d\u7528\u4e8e Alembic \u914d\u7f6e\u7684\u5c0f\u8282\u7684\u540d\u79f0  [default: alembic]\n  -x TEXT            \u81ea\u5b9a\u4e49 env.py \u811a\u672c\u4f7f\u7528\u7684\u5176\u4ed6\u53c2\u6570\uff0c\u4f8b\u5982\uff1a-x setting1=somesetting -x\n                     setting2=somesetting\n  -q, --quite        \u4e0d\u8981\u8f93\u51fa\u65e5\u5fd7\u5230\u6807\u51c6\u8f93\u51fa\n  --help             Show this message and exit.\n\nCommands:\n  branches        \u663e\u793a\u6240\u6709\u7684\u5206\u652f\u3002\n  check           \u68c0\u67e5\u6570\u636e\u5e93\u662f\u5426\u4e0e\u6a21\u578b\u5b9a\u4e49\u4e00\u81f4\u3002\n  current         \u663e\u793a\u5f53\u524d\u7684\u8fc1\u79fb\u3002\n  downgrade       \u56de\u9000\u5230\u5148\u524d\u7248\u672c\u3002\n  edit            \u4f7f\u7528 $EDITOR \u7f16\u8f91\u8fc1\u79fb\u811a\u672c\u3002\n  ensure_version  \u521b\u5efa\u7248\u672c\u8868\u3002\n  heads           \u663e\u793a\u6240\u6709\u7684\u5206\u652f\u5934\u3002\n  history         \u663e\u793a\u8fc1\u79fb\u7684\u5386\u53f2\u3002\n  init            \u521d\u59cb\u5316\u811a\u672c\u76ee\u5f55\u3002\n  list_templates  \u5217\u51fa\u6240\u6709\u53ef\u7528\u7684\u6a21\u677f\u3002\n  merge           \u5408\u5e76\u591a\u4e2a\u8fc1\u79fb\u3002\u521b\u5efa\u4e00\u4e2a\u65b0\u7684\u8fc1\u79fb\u811a\u672c\u3002\n  revision        \u521b\u5efa\u4e00\u4e2a\u65b0\u8fc1\u79fb\u811a\u672c\u3002\n  show            \u663e\u793a\u8fc1\u79fb\u7684\u4fe1\u606f\u3002\n  stamp           \u5c06\u6570\u636e\u5e93\u6807\u8bb0\u4e3a\u7279\u5b9a\u7684\u8fc1\u79fb\u7248\u672c\uff0c\u4e0d\u8fd0\u884c\u4efb\u4f55\u8fc1\u79fb\u3002\n  upgrade         \u5347\u7ea7\u5230\u8f83\u65b0\u7248\u672c\u3002\n```\n\n## \u914d\u7f6e\u9879\n\n### sqlalchemy_database_url\n\n\u9ed8\u8ba4\u6570\u636e\u5e93\u8fde\u63a5 URL\u3002\n\u53c2\u89c1\uff1a[Engine Configuration \u2014 SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/core/engines.html#database-urls)\n\n```properties\nSQLALCHEMY_DATABASE_URL=sqlite+aiosqlite://\n```\n\n### sqlalchemy_binds\n\nbind keys \u5230 `AsyncEngine` \u9009\u9879\u7684\u6620\u5c04\u3002\u503c\u53ef\u4ee5\u662f\u6570\u636e\u5e93\u8fde\u63a5 URL\u3001`AsyncEngine` \u9009\u9879\u5b57\u5178\u6216\u8005 `AsyncEngine` \u5b9e\u4f8b\u3002\n\n```properties\nSQLALCHEMY_BINDS='{\n    \"\": \"sqlite+aiosqlite://\",\n    \"nonebot_plugin_user\": {\n        \"url\": \"postgresql+asyncpg://scott:tiger@localhost/mydatabase\",\n        \"echo\": true\n    }\n}'\n```\n\n### sqlalchemy_echo\n\n\u6240\u6709 `AsyncEngine` \u7684 `echo` \u548c `echo_pool` \u9009\u9879\u7684\u9ed8\u8ba4\u503c\u3002\u7528\u4e8e\u5feb\u901f\u8c03\u8bd5\u8fde\u63a5\u548c SQL \u751f\u6210\u95ee\u9898\u3002\n\n```properties\nSQLALCHEMY_ECHO=true\n```\n\n### sqlalchemy_engine_options\n\n\u6240\u6709 `AsyncEngine` \u7684\u9ed8\u8ba4\u9009\u9879\u5b57\u5178\u3002\n\u53c2\u89c1\uff1a[Engine Configuration \u2014 SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/core/engines.html#engine-configuration)\n\n```properties\nSQLALCHEMY_ENGINE_OPTIONS='{\n    \"pool_size\": 5,\n    \"max_overflow\": 10,\n    \"pool_timeout\": 30,\n    \"pool_recycle\": 3600,\n    \"echo\": true\n}'\n```\n\n### sqlalchemy_session_options\n\n`AsyncSession` \u7684\u9009\u9879\u5b57\u5178\u3002\n\u53c2\u89c1\uff1a[Session API \u2014 SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/orm/session_api.html#sqlalchemy.orm.Session.__init__)\n\n```properties\nSQLALCHEMY_SESSION_OPTIONS='{\n    \"autoflush\": false,\n    \"autobegin\": true,\n    \"expire_on_commit\": true\n}'\n```\n\n### alembic_config\n\n\u914d\u7f6e\u6587\u4ef6\u8def\u5f84\u6216 `AlembicConfig` \u5b9e\u4f8b\u3002\n\n```properties\nALEMBIC_CONFIG=alembic.ini\n```\n\n### alembic_script_location\n\n\u811a\u672c\u76ee\u5f55\u8def\u5f84\u3002\n\n```properties\nALEMBIC_SCRIPT_LOCATION=migrations\n```\n\n### alembic_version_locations\n\n\u8fc1\u79fb\u811a\u672c\u76ee\u5f55\u8def\u5f84\u6216\u5206\u652f\u6807\u7b7e\u5230\u8fc1\u79fb\u811a\u672c\u76ee\u5f55\u8def\u5f84\u7684\u6620\u5c04\u3002\n\n```properties\nALEMBIC_VERSION_LOCATIONS=migrations/versions\n\nALEMBIC_VERSION_LOCATIONS='{\n    \"\": \"migrations/versions\",\n    \"nonebot_plugin_user\": \"src/nonebot_plugin_user/versions\",\n    \"nonebot_plugin_chatrecorder\": \"migrations/versions/nonebot_plugin_chatrecorder\"\n}'\n```\n\n### alembic_context\n\n`MigrationContext` \u7684\u9009\u9879\u5b57\u5178\u3002\n\u53c2\u89c1\uff1a[Runtime Objects \u2014 Alembic 1.12.0 documentation](https://alembic.sqlalchemy.org/en/latest/api/runtime.html#alembic.runtime.environment.EnvironmentContext.configure)\n\n```properties\nALEMBIC_CONTEXT='{\n    \"render_as_batch\": true\n}'\n```\n\n### alembic_startup_check\n\n\u662f\u5426\u5728\u542f\u52a8\u65f6\u68c0\u67e5\u6570\u636e\u5e93\u4e0e\u6a21\u578b\u5b9a\u4e49\u7684\u4e00\u81f4\u6027\u3002\n\n```properties\nALEMBIC_STARTUP_CHECK=true\n```\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "SQLAlchemy ORM support for nonebot",
    "version": "0.7.2",
    "project_urls": {
        "Documentation": "https://github.com/nonebot/plugin-orm",
        "Homepage": "https://github.com/nonebot/plugin-orm",
        "Repository": "https://github.com/nonebot/plugin-orm"
    },
    "split_keywords": [
        "nonebot",
        " orm",
        " sqlalchemy"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ca7bb5efde3410271a64cb91082cf1f0e23ac26d7e65204038bddceefe8bae23",
                "md5": "cafafa009279e370557f02ba53c2c11f",
                "sha256": "a9fce44d7d80a98b7e07125abe26529995fde967e1f9dd390180d70734f9c596"
            },
            "downloads": -1,
            "filename": "nonebot_plugin_orm-0.7.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "cafafa009279e370557f02ba53c2c11f",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.8",
            "size": 32366,
            "upload_time": "2024-04-28T09:59:28",
            "upload_time_iso_8601": "2024-04-28T09:59:28.522636Z",
            "url": "https://files.pythonhosted.org/packages/ca/7b/b5efde3410271a64cb91082cf1f0e23ac26d7e65204038bddceefe8bae23/nonebot_plugin_orm-0.7.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "24ac4cd4d6a79a7651cff8b393da773fcc40f0f65618b5eaf8dd0479e962dad7",
                "md5": "7fbafdc13d465864fdcfda067ad0277d",
                "sha256": "12f12e8b1be5d5f75bba0a630e5112ad4041b0ef67469dbcd7ac6557faec0953"
            },
            "downloads": -1,
            "filename": "nonebot_plugin_orm-0.7.2.tar.gz",
            "has_sig": false,
            "md5_digest": "7fbafdc13d465864fdcfda067ad0277d",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.8",
            "size": 27158,
            "upload_time": "2024-04-28T09:59:30",
            "upload_time_iso_8601": "2024-04-28T09:59:30.515042Z",
            "url": "https://files.pythonhosted.org/packages/24/ac/4cd4d6a79a7651cff8b393da773fcc40f0f65618b5eaf8dd0479e962dad7/nonebot_plugin_orm-0.7.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-28 09:59:30",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "nonebot",
    "github_project": "plugin-orm",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "nonebot-plugin-orm"
}
        
Elapsed time: 0.34546s