2023-09-28 20:19:40 +03:00
|
|
|
{
|
|
|
|
pkgs ? import <nixpkgs> {},
|
|
|
|
lib ? import <nixpkgs/lib>,
|
2023-12-04 10:15:38 +03:00
|
|
|
dream2nix ? (import ../../../.),
|
|
|
|
inputs ? (import ../../../.).inputs,
|
2023-09-28 20:19:40 +03:00
|
|
|
}: let
|
2023-09-28 21:15:42 +03:00
|
|
|
libpdm = (import ../../../modules/dream2nix/WIP-python-pdm/lib.nix) {
|
2023-09-28 20:19:40 +03:00
|
|
|
inherit lib libpyproject;
|
2023-12-04 10:15:38 +03:00
|
|
|
python3 = pkgs.python3;
|
|
|
|
targetPlatform = lib.systems.elaborate "x86_64-linux";
|
2023-09-28 20:19:40 +03:00
|
|
|
};
|
|
|
|
pyproject-nix = inputs.pyproject-nix;
|
|
|
|
libpyproject = import (pyproject-nix + "/lib") {inherit lib;};
|
|
|
|
|
2023-09-29 16:22:26 +03:00
|
|
|
linux_environ = lib.importJSON ./environ.json;
|
|
|
|
|
2023-12-04 10:15:38 +03:00
|
|
|
testIsUsableSdistFilename = filename: let
|
|
|
|
environ = linux_environ;
|
|
|
|
in
|
|
|
|
libpdm.isUsableSdistFilename {inherit environ filename;};
|
|
|
|
in {
|
|
|
|
isDependencyRequired = {
|
2023-09-29 16:22:26 +03:00
|
|
|
test_isDependencyRequired__not_required = {
|
|
|
|
expr =
|
|
|
|
libpdm.isDependencyRequired
|
|
|
|
linux_environ
|
|
|
|
(libpyproject.pep508.parseString "foo==1.0.0; sys_platform == 'win32'");
|
|
|
|
expected = false;
|
|
|
|
};
|
|
|
|
test_isDependencyRequired__required = {
|
|
|
|
expr =
|
|
|
|
libpdm.isDependencyRequired
|
|
|
|
linux_environ
|
|
|
|
(libpyproject.pep508.parseString "foo==1.0.0; sys_platform == 'linux'");
|
|
|
|
expected = true;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-12-04 10:15:38 +03:00
|
|
|
isUsableFilename = let
|
2023-09-28 20:19:40 +03:00
|
|
|
testIsUsableWheelFilename = filename: let
|
2023-09-29 22:06:18 +03:00
|
|
|
environ = linux_environ;
|
2023-09-28 20:19:40 +03:00
|
|
|
in
|
|
|
|
libpdm.isUsableWheelFilename {inherit environ filename;};
|
|
|
|
in {
|
|
|
|
test_isUsableFilename__sdist = {
|
|
|
|
expr = testIsUsableSdistFilename "certifi-2023.7.22.tar.gz";
|
|
|
|
expected = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
test_isUsableFilename__wheel_universal = {
|
|
|
|
expr = testIsUsableWheelFilename "certifi-2023.7.22-py3-none-any.whl";
|
|
|
|
expected = true;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-12-04 10:15:38 +03:00
|
|
|
selectExtension = let
|
2023-09-28 20:19:40 +03:00
|
|
|
names = [
|
|
|
|
"certifi-2023.7.22-py3-abi3-any.whl"
|
|
|
|
"certifi-2023.7.22.tar.gz"
|
|
|
|
"certifi-2023.7.22.zip"
|
|
|
|
"certifi-2023.7.22.zip"
|
|
|
|
];
|
|
|
|
in {
|
|
|
|
test_selectExtension__tar_gz = {
|
|
|
|
expr = libpdm.selectExtension names ".tar.gz";
|
|
|
|
expected = "certifi-2023.7.22.tar.gz";
|
|
|
|
};
|
|
|
|
test_selectExtension__zip = let
|
|
|
|
extension = ".zip";
|
|
|
|
in {
|
|
|
|
expr = libpdm.selectExtension names extension;
|
|
|
|
expectedError.type = "ThrownError";
|
|
|
|
expectedError.msg = "Multiple names found with extension ${extension}";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-12-04 10:15:38 +03:00
|
|
|
selectSdist = let
|
2023-09-28 20:19:40 +03:00
|
|
|
names = [
|
|
|
|
"certifi-2023.7.22-py3-abi3-any.whl"
|
|
|
|
"certifi-2023.7.22.tar.gz"
|
|
|
|
"certifi-2023.7.22.zip"
|
|
|
|
"certifi-2023.7.22.zip"
|
|
|
|
];
|
|
|
|
in {
|
2023-12-10 08:20:04 +03:00
|
|
|
test_tar_gz = {
|
2023-09-28 20:19:40 +03:00
|
|
|
expr = libpdm.selectSdist names;
|
|
|
|
expected = "certifi-2023.7.22.tar.gz";
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
test_no_sdist = let
|
2023-09-28 20:19:40 +03:00
|
|
|
names = [
|
|
|
|
"certifi-2023.7.22-py3-abi3-any.whl"
|
|
|
|
];
|
|
|
|
in {
|
|
|
|
expr = libpdm.selectSdist names;
|
|
|
|
expected = null;
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
test_order = let
|
2023-09-28 20:19:40 +03:00
|
|
|
names = [
|
|
|
|
"certifi-2023.7.22.zip"
|
|
|
|
"certifi-2023.7.22.tar.gz"
|
|
|
|
];
|
|
|
|
in {
|
|
|
|
expr = libpdm.selectSdist names;
|
|
|
|
expected = "certifi-2023.7.22.tar.gz";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-12-04 10:15:38 +03:00
|
|
|
selectWheel = {
|
|
|
|
test_empty = {
|
|
|
|
expr = libpdm.selectWheel [];
|
|
|
|
expected = null;
|
|
|
|
};
|
|
|
|
test_simple_call = {
|
|
|
|
expr = libpdm.selectWheel ["charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl"];
|
|
|
|
expected = null;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
preferWheelSelector = {
|
2023-12-10 08:20:04 +03:00
|
|
|
test_has_wheel = let
|
2023-09-28 20:19:40 +03:00
|
|
|
names = [
|
|
|
|
"certifi-2023.7.22-py3-abi3-any.whl"
|
|
|
|
"certifi-2023.7.22.tar.gz"
|
|
|
|
"certifi-2023.7.22.zip"
|
|
|
|
];
|
|
|
|
in {
|
|
|
|
expr = libpdm.preferWheelSelector names;
|
|
|
|
expected = "certifi-2023.7.22-py3-abi3-any.whl";
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
test_only_sdist = let
|
2023-09-28 20:19:40 +03:00
|
|
|
names = [
|
|
|
|
"certifi-2023.7.22.zip"
|
|
|
|
"certifi-2023.7.22.tar.gz"
|
|
|
|
];
|
|
|
|
in {
|
|
|
|
expr = libpdm.preferWheelSelector names;
|
|
|
|
expected = "certifi-2023.7.22.tar.gz";
|
|
|
|
};
|
|
|
|
};
|
2023-12-04 10:15:38 +03:00
|
|
|
|
|
|
|
preferSdistSelector = {
|
2023-12-10 08:20:04 +03:00
|
|
|
test_has_sdist = let
|
2023-09-28 20:19:40 +03:00
|
|
|
names = [
|
|
|
|
"certifi-2023.7.22-py3-abi3-any.whl"
|
|
|
|
"certifi-2023.7.22.tar.gz"
|
|
|
|
"certifi-2023.7.22.zip"
|
|
|
|
];
|
|
|
|
in {
|
|
|
|
expr = libpdm.preferSdistSelector names;
|
|
|
|
expected = "certifi-2023.7.22.tar.gz";
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
test_only_sdist = let
|
2023-09-28 20:19:40 +03:00
|
|
|
names = [
|
|
|
|
"certifi-2023.7.22.zip"
|
|
|
|
"certifi-2023.7.22.tar.gz"
|
|
|
|
];
|
|
|
|
in {
|
|
|
|
expr = libpdm.preferSdistSelector names;
|
|
|
|
expected = "certifi-2023.7.22.tar.gz";
|
|
|
|
};
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
|
|
|
|
parsePackage = {
|
|
|
|
test_simple = {
|
|
|
|
expr = libpdm.parsePackage linux_environ {
|
|
|
|
name = "foo";
|
|
|
|
version = "1.0.0";
|
|
|
|
extras = ["extra1" "extra2"];
|
|
|
|
requires_python = ">=3.9";
|
|
|
|
files = [
|
|
|
|
{
|
|
|
|
file = "foo-1.0.0-py3-none-any.whl";
|
|
|
|
hash = "sha256:foo";
|
|
|
|
}
|
|
|
|
{
|
|
|
|
file = "foo-1.0.0.tar.gz";
|
|
|
|
hash = "sha256:bar";
|
|
|
|
}
|
|
|
|
];
|
|
|
|
dependencies = [
|
|
|
|
"bar[security,performance]==1.0.0"
|
|
|
|
];
|
|
|
|
};
|
|
|
|
expected = {
|
|
|
|
name = "foo";
|
|
|
|
version = "1.0.0";
|
|
|
|
extras = ["extra1" "extra2"];
|
|
|
|
dependencies = [
|
|
|
|
{
|
|
|
|
conditions = [
|
|
|
|
{
|
|
|
|
op = "==";
|
|
|
|
version = {
|
|
|
|
dev = null;
|
|
|
|
epoch = 0;
|
|
|
|
local = null;
|
|
|
|
post = null;
|
|
|
|
pre = null;
|
|
|
|
release = [1 0 0];
|
|
|
|
};
|
|
|
|
}
|
|
|
|
];
|
|
|
|
extras = ["security" "performance"];
|
|
|
|
markers = null;
|
|
|
|
name = "bar";
|
|
|
|
url = null;
|
|
|
|
}
|
|
|
|
];
|
|
|
|
sources = {
|
|
|
|
"foo-1.0.0-py3-none-any.whl" = {
|
|
|
|
file = "foo-1.0.0-py3-none-any.whl";
|
|
|
|
hash = "sha256:foo";
|
|
|
|
};
|
|
|
|
"foo-1.0.0.tar.gz" = {
|
|
|
|
file = "foo-1.0.0.tar.gz";
|
|
|
|
hash = "sha256:bar";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-12-04 10:15:38 +03:00
|
|
|
parseLockData = let
|
2023-12-10 08:20:04 +03:00
|
|
|
lock_data = lib.importTOML ./fixtures/pdm-example1.lock;
|
2023-09-29 16:22:26 +03:00
|
|
|
version = "2.31.0";
|
2023-09-28 22:42:34 +03:00
|
|
|
parsed = libpdm.parseLockData {
|
2023-09-29 22:06:18 +03:00
|
|
|
inherit lock_data;
|
|
|
|
environ = linux_environ;
|
2023-09-28 22:42:34 +03:00
|
|
|
};
|
2023-09-28 20:19:40 +03:00
|
|
|
in {
|
2023-12-10 08:20:04 +03:00
|
|
|
test_simple = {
|
2023-09-28 22:42:34 +03:00
|
|
|
expr =
|
2023-09-29 16:22:26 +03:00
|
|
|
(parsed ? "requests")
|
2023-12-10 08:20:04 +03:00
|
|
|
&& (parsed.requests.default.version == version)
|
|
|
|
&& (parsed.requests.default ? sources);
|
2023-09-28 22:42:34 +03:00
|
|
|
expected = true;
|
2023-09-28 20:19:40 +03:00
|
|
|
};
|
2023-09-29 16:22:26 +03:00
|
|
|
|
2023-12-10 08:20:04 +03:00
|
|
|
test_file = {
|
|
|
|
expr = libpdm.preferWheelSelector (lib.attrNames parsed.requests.default.sources);
|
2023-09-29 16:22:26 +03:00
|
|
|
expected = "requests-2.31.0-py3-none-any.whl";
|
|
|
|
};
|
|
|
|
|
2023-12-10 08:20:04 +03:00
|
|
|
test_dependencies = {
|
|
|
|
expr = map (dep: dep.name) parsed.requests.default.dependencies;
|
2023-09-29 16:22:26 +03:00
|
|
|
expected = [
|
|
|
|
"certifi"
|
|
|
|
"charset-normalizer"
|
|
|
|
"idna"
|
|
|
|
"urllib3"
|
|
|
|
];
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
test_candidates_with_different_extras = rec {
|
|
|
|
expr = libpdm.parseLockData {
|
|
|
|
environ = linux_environ;
|
|
|
|
lock_data = {
|
|
|
|
package = [
|
|
|
|
{
|
|
|
|
name = "foo";
|
|
|
|
version = "1.0.0";
|
|
|
|
extras = [];
|
|
|
|
dependencies = [
|
|
|
|
"dep1==1.0.0"
|
|
|
|
];
|
|
|
|
files = [
|
|
|
|
{
|
|
|
|
file = "foo-1.0.0.tar.gz";
|
|
|
|
hash = "sha256:bar";
|
|
|
|
}
|
|
|
|
];
|
|
|
|
}
|
|
|
|
{
|
|
|
|
name = "foo";
|
|
|
|
version = "1.0.0";
|
|
|
|
extras = ["extra1" "extra2"];
|
|
|
|
dependencies = [
|
|
|
|
"extradep1==1.0.0"
|
|
|
|
];
|
|
|
|
files = [
|
|
|
|
{
|
|
|
|
file = "foo-1.0.0.tar.gz";
|
|
|
|
hash = "sha256:bar";
|
|
|
|
}
|
|
|
|
];
|
|
|
|
}
|
|
|
|
];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
expected = {
|
|
|
|
foo = {
|
|
|
|
default = {
|
|
|
|
name = "foo";
|
|
|
|
version = "1.0.0";
|
|
|
|
extras = [];
|
|
|
|
dependencies = expr.foo.default.dependencies;
|
|
|
|
sources = expr.foo.default.sources;
|
|
|
|
};
|
|
|
|
"extra1,extra2" = {
|
|
|
|
name = "foo";
|
|
|
|
version = "1.0.0";
|
|
|
|
extras = ["extra1" "extra2"];
|
|
|
|
dependencies = expr.foo."extra1,extra2".dependencies;
|
|
|
|
sources = expr.foo."extra1,extra2".sources;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
2023-09-28 20:19:40 +03:00
|
|
|
};
|
2023-12-04 10:15:38 +03:00
|
|
|
groupsWithDeps = let
|
2023-09-29 22:06:18 +03:00
|
|
|
environ = linux_environ;
|
2023-12-10 08:20:04 +03:00
|
|
|
pyproject = libpdm.loadPdmPyProject (lib.importTOML ./fixtures/pyproject.toml);
|
2023-09-29 22:06:18 +03:00
|
|
|
groups_with_deps = libpdm.groupsWithDeps {
|
|
|
|
inherit environ pyproject;
|
|
|
|
};
|
|
|
|
in {
|
2023-12-10 08:20:04 +03:00
|
|
|
test_has_main_group = {
|
2023-09-29 22:06:18 +03:00
|
|
|
expr = groups_with_deps ? "default";
|
|
|
|
expected = true;
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
test_main_group_has_deps = {
|
|
|
|
expr = map (dep: dep.name) groups_with_deps.default;
|
2023-09-29 22:06:18 +03:00
|
|
|
expected = ["requests"];
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
test_optionals_dev_has_deps = {
|
|
|
|
expr = map (dep: dep.name) groups_with_deps.dev;
|
2023-09-29 22:06:18 +03:00
|
|
|
expected = ["pi"];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-12-10 08:20:04 +03:00
|
|
|
getClosure = let
|
2023-09-29 22:06:18 +03:00
|
|
|
environ = linux_environ;
|
2023-12-10 08:20:04 +03:00
|
|
|
lock_data_2 = lib.importTOML ./fixtures/pdm-example1.lock;
|
|
|
|
parsed_lock_data_2 = libpdm.parseLockData {
|
|
|
|
inherit environ;
|
|
|
|
lock_data = lock_data_2;
|
2023-09-29 22:06:18 +03:00
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
in rec {
|
|
|
|
deps = libpdm.getClosure parsed_lock_data_2 "requests" [];
|
|
|
|
|
|
|
|
test_versions = {
|
|
|
|
expr = lib.mapAttrs (name: dep: dep.version) deps;
|
2023-09-29 22:06:18 +03:00
|
|
|
expected = {
|
|
|
|
certifi = "2023.7.22";
|
|
|
|
charset-normalizer = "3.2.0";
|
|
|
|
idna = "3.4";
|
|
|
|
urllib3 = "2.0.5";
|
|
|
|
};
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
|
|
|
|
test_sources = let
|
2023-12-04 10:15:38 +03:00
|
|
|
selectFilename = sources: libpdm.preferWheelSelector (lib.attrNames sources);
|
|
|
|
in {
|
2023-12-10 08:20:04 +03:00
|
|
|
expr = lib.mapAttrs (name: dep: selectFilename dep.sources) deps;
|
2023-09-29 22:06:18 +03:00
|
|
|
expected = {
|
|
|
|
certifi = "certifi-2023.7.22-py3-none-any.whl";
|
2023-12-04 10:15:38 +03:00
|
|
|
charset-normalizer = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl";
|
2023-09-29 22:06:18 +03:00
|
|
|
idna = "idna-3.4-py3-none-any.whl";
|
|
|
|
urllib3 = "urllib3-2.0.5-py3-none-any.whl";
|
|
|
|
};
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
|
|
|
|
test_no_cycles = let
|
|
|
|
lock_data = lib.importTOML ./fixtures/pdm-with-cycles.lock;
|
|
|
|
parsed_lock_data = libpdm.parseLockData {
|
|
|
|
inherit environ lock_data;
|
|
|
|
};
|
|
|
|
deps = libpdm.getClosure parsed_lock_data "pyjwt" ["crypto"];
|
|
|
|
in {
|
|
|
|
expr = lib.mapAttrs (name: dep: dep.version) deps;
|
|
|
|
expected = {
|
|
|
|
cffi = "1.16.0";
|
|
|
|
cryptography = "41.0.7";
|
|
|
|
pycparser = "2.21";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
test_closure_collects_all_extras = let
|
|
|
|
lock_data = lib.importTOML ./fixtures/pdm-extras.lock;
|
|
|
|
parsed_lock_data = libpdm.parseLockData {
|
|
|
|
inherit environ lock_data;
|
|
|
|
};
|
|
|
|
deps = libpdm.getClosure parsed_lock_data "root" [];
|
|
|
|
in {
|
|
|
|
expr.foo = deps.foo.extras;
|
|
|
|
expr.bar = deps.bar.extras;
|
|
|
|
expected = {
|
|
|
|
foo = ["extra1"];
|
|
|
|
bar = ["extra2"];
|
|
|
|
};
|
|
|
|
};
|
2023-09-29 22:06:18 +03:00
|
|
|
};
|
|
|
|
|
2023-12-10 08:20:04 +03:00
|
|
|
closureForGroups = let
|
2023-09-29 22:06:18 +03:00
|
|
|
environ = linux_environ;
|
2023-12-10 08:20:04 +03:00
|
|
|
pyproject = libpdm.loadPdmPyProject (lib.importTOML ./fixtures/pyproject.toml);
|
|
|
|
lock_data = lib.importTOML ./fixtures/pdm-example1.lock;
|
2023-09-29 22:06:18 +03:00
|
|
|
groups_with_deps = libpdm.groupsWithDeps {
|
|
|
|
inherit environ pyproject;
|
|
|
|
};
|
|
|
|
parsed_lock_data = libpdm.parseLockData {
|
|
|
|
inherit lock_data;
|
|
|
|
environ = linux_environ;
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
deps_default = libpdm.closureForGroups {
|
2023-09-29 22:06:18 +03:00
|
|
|
inherit parsed_lock_data groups_with_deps;
|
2023-10-29 22:34:13 +03:00
|
|
|
groupNames = ["default"];
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
deps_dev = libpdm.closureForGroups {
|
2023-10-29 22:34:13 +03:00
|
|
|
inherit parsed_lock_data groups_with_deps;
|
|
|
|
groupNames = ["default" "dev"];
|
2023-09-29 22:06:18 +03:00
|
|
|
};
|
|
|
|
in {
|
2023-12-10 08:20:04 +03:00
|
|
|
test_names = {
|
2023-09-29 22:06:18 +03:00
|
|
|
expr = lib.attrNames deps_default;
|
|
|
|
expected = ["certifi" "charset-normalizer" "idna" "requests" "urllib3"];
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
test_versions = {
|
2023-09-29 22:06:18 +03:00
|
|
|
expr = lib.mapAttrs (key: value: value.version) deps_default;
|
|
|
|
expected = {
|
|
|
|
certifi = "2023.7.22";
|
|
|
|
charset-normalizer = "3.2.0";
|
|
|
|
idna = "3.4";
|
|
|
|
requests = "2.31.0";
|
|
|
|
urllib3 = "2.0.5";
|
|
|
|
};
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
test_closureForGroups_sources = let
|
2023-12-04 10:15:38 +03:00
|
|
|
selectFilename = sources: libpdm.preferWheelSelector (lib.attrNames sources);
|
|
|
|
in {
|
|
|
|
expr = lib.mapAttrs (key: value: selectFilename value.sources) deps_default;
|
2023-09-29 22:06:18 +03:00
|
|
|
expected = {
|
|
|
|
certifi = "certifi-2023.7.22-py3-none-any.whl";
|
2023-12-04 10:15:38 +03:00
|
|
|
charset-normalizer = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl";
|
2023-09-29 22:06:18 +03:00
|
|
|
idna = "idna-3.4-py3-none-any.whl";
|
|
|
|
requests = "requests-2.31.0-py3-none-any.whl";
|
|
|
|
urllib3 = "urllib3-2.0.5-py3-none-any.whl";
|
|
|
|
};
|
|
|
|
};
|
2023-12-10 08:20:04 +03:00
|
|
|
test_dev_versions = {
|
2023-10-29 22:34:13 +03:00
|
|
|
expr = lib.mapAttrs (key: value: value.version) deps_dev;
|
|
|
|
expected = {
|
|
|
|
certifi = "2023.7.22";
|
|
|
|
charset-normalizer = "3.2.0";
|
|
|
|
idna = "3.4";
|
|
|
|
requests = "2.31.0";
|
|
|
|
urllib3 = "2.0.5";
|
|
|
|
pi = "0.1.2";
|
|
|
|
};
|
|
|
|
};
|
2023-09-29 22:06:18 +03:00
|
|
|
};
|
2023-12-04 10:15:38 +03:00
|
|
|
}
|