ai_v/venv/Lib/site-packages/tzlocal/unix.py

250 lines
8.9 KiB
Python
Raw Normal View History

feat(api): 实现图像生成及后台同步功能 - 新增图像生成接口,支持试用、积分和自定义API Key模式 - 实现生成图片结果异步上传至MinIO存储,带重试机制 - 优化积分预扣除和异常退还逻辑,保障用户积分准确 - 添加获取生成历史记录接口,支持时间范围和分页 - 提供本地字典配置接口,支持模型、比例、提示模板和尺寸 - 实现图片批量上传接口,支持S3兼容对象存储 feat(admin): 增加管理员角色管理与权限分配接口 - 实现角色列表查询、角色创建、更新及删除功能 - 增加权限列表查询接口 - 实现用户角色分配接口,便于统一管理用户权限 - 增加系统字典增删查改接口,支持分类过滤和排序 - 权限控制全面覆盖管理接口,保证安全访问 feat(auth): 完善用户登录注册及权限相关接口与页面 - 实现手机号验证码发送及校验功能,保障注册安全 - 支持手机号注册、登录及退出接口,集成日志记录 - 增加修改密码功能,验证原密码后更新 - 提供动态导航菜单接口,基于权限展示不同菜单 - 实现管理界面路由及日志、角色、字典管理页面访问权限控制 - 添加系统日志查询接口,支持关键词和等级筛选 feat(app): 初始化Flask应用并配置蓝图与数据库 - 创建应用程序工厂,加载配置,初始化数据库和Redis客户端 - 注册认证、API及管理员蓝图,整合路由 - 根路由渲染主页模板 - 应用上下文中自动创建数据库表,保证运行环境准备完毕 feat(database): 提供数据库创建与迁移支持脚本 - 新增数据库创建脚本,支持自动检测是否已存在 - 添加数据库表初始化脚本,支持创建和删除所有表 - 实现RBAC权限初始化,包含基础权限和角色创建 - 新增字段手动修复脚本,添加用户API Key和积分字段 - 强制迁移脚本支持清理连接和修复表结构,初始化默认数据及角色分配 feat(config): 新增系统配置参数 - 配置数据库、Redis、Session和MinIO相关参数 - 添加AI接口地址及试用Key配置 - 集成阿里云短信服务配置及开发模式相关参数 feat(extensions): 初始化数据库、Redis和MinIO客户端 - 创建全局SQLAlchemy数据库实例和Redis客户端 - 配置基于boto3的MinIO兼容S3客户端 chore(logs): 添加示例系统日志文件 - 记录用户请求、验证码发送成功与失败的日志信息
2026-01-12 00:53:31 +08:00
import logging
import os
import re
import sys
import warnings
from datetime import timezone
from tzlocal import utils
import zoneinfo
_cache_tz = None
_cache_tz_name = None
log = logging.getLogger("tzlocal")
def _get_localzone_name(_root="/"):
"""Tries to find the local timezone configuration.
This method finds the timezone name, if it can, or it returns None.
The parameter _root makes the function look for files like /etc/localtime
beneath the _root directory. This is primarily used by the tests.
In normal usage you call the function without parameters."""
# First try the ENV setting.
tzenv = utils._tz_name_from_env()
if tzenv:
return tzenv
# Are we under Termux on Android?
if os.path.exists(os.path.join(_root, "system/bin/getprop")):
log.debug("This looks like Termux")
import subprocess
try:
androidtz = (
subprocess.check_output(["getprop", "persist.sys.timezone"])
.strip()
.decode()
)
return androidtz
except (OSError, subprocess.CalledProcessError):
# proot environment or failed to getprop
log.debug("It's not termux?")
pass
# Now look for distribution specific configuration files
# that contain the timezone name.
# Stick all of them in a dict, to compare later.
found_configs = {}
for configfile in ("etc/timezone", "var/db/zoneinfo"):
tzpath = os.path.join(_root, configfile)
try:
with open(tzpath) as tzfile:
data = tzfile.read()
log.debug(f"{tzpath} found, contents:\n {data}")
etctz = data.strip("/ \t\r\n")
if not etctz:
# Empty file, skip
continue
for etctz in etctz.splitlines():
# Get rid of host definitions and comments:
if " " in etctz:
etctz, dummy = etctz.split(" ", 1)
if "#" in etctz:
etctz, dummy = etctz.split("#", 1)
if not etctz:
continue
found_configs[tzpath] = etctz.replace(" ", "_")
except (OSError, UnicodeDecodeError):
# File doesn't exist or is a directory, or it's a binary file.
continue
# CentOS has a ZONE setting in /etc/sysconfig/clock,
# OpenSUSE has a TIMEZONE setting in /etc/sysconfig/clock and
# Gentoo has a TIMEZONE setting in /etc/conf.d/clock
# We look through these files for a timezone:
zone_re = re.compile(r"\s*ZONE\s*=\s*\"")
timezone_re = re.compile(r"\s*TIMEZONE\s*=\s*\"")
end_re = re.compile('"')
for filename in ("etc/sysconfig/clock", "etc/conf.d/clock"):
tzpath = os.path.join(_root, filename)
try:
with open(tzpath, "rt") as tzfile:
data = tzfile.readlines()
log.debug(f"{tzpath} found, contents:\n {data}")
for line in data:
# Look for the ZONE= setting.
match = zone_re.match(line)
if match is None:
# No ZONE= setting. Look for the TIMEZONE= setting.
match = timezone_re.match(line)
if match is not None:
# Some setting existed
line = line[match.end() :]
etctz = line[: end_re.search(line).start()]
# We found a timezone
found_configs[tzpath] = etctz.replace(" ", "_")
except (OSError, UnicodeDecodeError):
# UnicodeDecode handles when clock is symlink to /etc/localtime
continue
# systemd distributions use symlinks that include the zone name,
# see manpage of localtime(5) and timedatectl(1)
tzpath = os.path.join(_root, "etc/localtime")
if os.path.exists(tzpath) and os.path.islink(tzpath):
log.debug(f"{tzpath} found")
etctz = os.path.realpath(tzpath)
start = etctz.find("/") + 1
while start != 0:
etctz = etctz[start:]
try:
zoneinfo.ZoneInfo(etctz)
tzinfo = f"{tzpath} is a symlink to"
found_configs[tzinfo] = etctz.replace(" ", "_")
# Only need first valid relative path in simlink.
break
except zoneinfo.ZoneInfoNotFoundError:
pass
start = etctz.find("/") + 1
if len(found_configs) > 0:
log.debug(f"{len(found_configs)} found:\n {found_configs}")
# We found some explicit config of some sort!
if len(found_configs) > 1:
# Uh-oh, multiple configs. See if they match:
unique_tzs = _get_unique_tzs(found_configs, _root)
if len(unique_tzs) != 1 and "etc/timezone" in str(found_configs.keys()):
# For some reason some distros are removing support for /etc/timezone,
# which is bad, because that's the only place where the timezone is stated
# in plain text, and what's worse, they don't delete it. So we can't trust
# it now, so when we have conflicting configs, we just ignore it, with a warning.
log.warning("/etc/timezone is deprecated in some distros, and no longer reliable. "
"tzlocal is ignoring it, and you can likely delete it.")
found_configs = {k: v for k, v in found_configs.items() if "etc/timezone" not in k}
unique_tzs = _get_unique_tzs(found_configs, _root)
if len(unique_tzs) != 1:
message = "Multiple conflicting time zone configurations found:\n"
for key, value in found_configs.items():
message += f"{key}: {value}\n"
message += "Fix the configuration, or set the time zone in a TZ environment variable.\n"
raise zoneinfo.ZoneInfoNotFoundError(message)
# We found exactly one config! Use it.
return list(found_configs.values())[0]
def _get_unique_tzs(found_configs, _root):
unique_tzs = set()
zoneinfopath = os.path.join(_root, "usr", "share", "zoneinfo")
directory_depth = len(zoneinfopath.split(os.path.sep))
for tzname in found_configs.values():
# Look them up in /usr/share/zoneinfo, and find what they
# really point to:
path = os.path.realpath(os.path.join(zoneinfopath, *tzname.split("/")))
real_zone_name = "/".join(path.split(os.path.sep)[directory_depth:])
unique_tzs.add(real_zone_name)
return unique_tzs
def _get_localzone(_root="/"):
"""Creates a timezone object from the timezone name.
If there is no timezone config, it will try to create a file from the
localtime timezone, and if there isn't one, it will default to UTC.
The parameter _root makes the function look for files like /etc/localtime
beneath the _root directory. This is primarily used by the tests.
In normal usage you call the function without parameters."""
# First try the ENV setting.
tzenv = utils._tz_from_env()
if tzenv:
return tzenv
tzname = _get_localzone_name(_root)
if tzname is None:
# No explicit setting existed. Use localtime
log.debug("No explicit setting existed. Use localtime")
for filename in ("etc/localtime", "usr/local/etc/localtime"):
tzpath = os.path.join(_root, filename)
if not os.path.exists(tzpath):
continue
with open(tzpath, "rb") as tzfile:
tz = zoneinfo.ZoneInfo.from_file(tzfile, key="local")
break
else:
warnings.warn("Can not find any timezone configuration, defaulting to UTC.")
utcname = [x for x in zoneinfo.available_timezones() if "UTC" in x]
if utcname:
tz = zoneinfo.ZoneInfo(utcname[0])
else:
tz = timezone.utc
else:
tz = zoneinfo.ZoneInfo(tzname)
if _root == "/":
# We are using a file in etc to name the timezone.
# Verify that the timezone specified there is actually used:
utils.assert_tz_offset(tz, error=False)
return tz
def get_localzone_name() -> str:
"""Get the computers configured local timezone name, if any."""
global _cache_tz_name
if _cache_tz_name is None:
_cache_tz_name = _get_localzone_name()
return _cache_tz_name
def get_localzone() -> zoneinfo.ZoneInfo:
"""Get the computers configured local timezone, if any."""
global _cache_tz
if _cache_tz is None:
_cache_tz = _get_localzone()
return _cache_tz
def reload_localzone() -> zoneinfo.ZoneInfo:
"""Reload the cached localzone. You need to call this if the timezone has changed."""
global _cache_tz_name
global _cache_tz
_cache_tz_name = _get_localzone_name()
_cache_tz = _get_localzone()
return _cache_tz