Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ce1ad58
chore: [repokit] add pyproject.toml
Jun 19, 2026
c795b9e
chore: [repokit] save language config
Jun 19, 2026
0aa7ff9
chore: [repokit] update ci workflows
Jun 19, 2026
3a57a9f
chore: [repokit] update ci workflows
Jun 21, 2026
a57c1a3
fix: Fixed repokit dependencies
djachenko Jun 21, 2026
c7d9170
fix: Fixed ruff errors
djachenko Jun 21, 2026
269f5b1
Merge branch 'master' into chore/repokit-setup
djachenko Jun 21, 2026
b9f4da0
fix: groups imports — remove tokenize shim, add Any/TYPE_CHECKING
djachenko Jun 21, 2026
19374ca
refactor: add api fail-fast property to ApiMixin and ApiBased
djachenko Jun 21, 2026
c5f8054
fix: remove API=Any shims, replace api: API type hints with Any
djachenko Jun 21, 2026
d0da0ca
fix: migrate pyvko_main and events self.api → self.new_api
djachenko Jun 21, 2026
4b83662
chore: exclude pyvko_runner from mypy, add BACKLOG
djachenko Jun 21, 2026
1ca5214
fix: Dict = None → Dict | None across api_based, albums, reposts
djachenko Jun 21, 2026
2409ec1
fix: nullable fields in PostModel, Post, CommentModel
djachenko Jun 21, 2026
d9f8445
fix: Link stub properties raise NotImplementedError, photo_object typ…
djachenko Jun 21, 2026
71912f8
fix: get_all and PhotoUploader type signatures
djachenko Jun 21, 2026
dcb16e4
refactor: migrate to src layout
djachenko Jun 21, 2026
c9c21f8
chore: ignore missing stubs globally, document in BACKLOG
djachenko Jun 21, 2026
86a4a5e
chore: [repokit] update ci workflows
Jun 24, 2026
47d47b3
fix: precise mypy override for vk_api, remove types-requests from deps
djachenko Jun 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Integration

on:
pull_request:
branches:
- master

jobs:
integration:
uses: djachenko/repokit/.github/workflows/python-integration.yml@0.5
secrets: inherit

publish:
needs: integration
runs-on: ubuntu-latest
permissions:
id-token: write

steps:
- uses: actions/download-artifact@v8
with:
name: dist
path: dist/

- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
skip-existing: true

- name: Smoke test
run: |
PACKAGE=$(ls dist/*.whl | head -1 | xargs basename | sed 's/-[0-9].*//')
VERSION=$(ls dist/*.tar.gz | sed 's/.*-\(.*\)\.tar\.gz/\1/')
for i in {1..5}; do
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ $PACKAGE==$VERSION && break
echo "Attempt $i failed, retrying in 10s..."
sleep 10
done
python -c "import $PACKAGE"
40 changes: 40 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Release

on:
push:
branches:
- master

jobs:
release:
uses: djachenko/repokit/.github/workflows/python-release.yml@0.5
secrets: inherit
permissions:
contents: write

publish:
needs: release
if: needs.release.outputs.released == 'true'
runs-on: ubuntu-latest
permissions:
id-token: write

steps:
- uses: actions/download-artifact@v8
with:
name: dist
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

- name: Smoke test
run: |
PACKAGE=$(ls dist/*.whl | head -1 | xargs basename | sed 's/-[0-9].*//')
VERSION=$(ls dist/*.tar.gz | sed 's/.*-\(.*\)\.tar\.gz/\1/')
for i in {1..5}; do
pip install $PACKAGE==$VERSION && break
echo "Attempt $i failed, retrying in 10s..."
sleep 10
done
python -c "import $PACKAGE"
13 changes: 13 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Tests

on:
push:
branches:
- '**'
tags-ignore:
- '**'

jobs:
tests:
uses: djachenko/repokit/.github/workflows/python-tests.yml@0.5
secrets: inherit
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ pyvko\.egg-info/
test_photos/

build/
.repokit
7 changes: 7 additions & 0 deletions BACKLOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Backlog



## mypy exclusions (нужно вернуть)

- `pyvko/pyvko_runner.py` — исключён через `[[tool.mypy.overrides]] ignore_errors = true` в pyproject.toml. Файл содержит старый демо-код с ~20 реальными ошибками (устаревший API, неверные типы). Нужно либо выпилить файл, либо привести в соответствие с текущим API.
68 changes: 34 additions & 34 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
[build-system]
requires = ["setuptools>=61.0", "wheel"]
requires = ["setuptools>=77.0.3"]
build-backend = "setuptools.build_meta"

[project]
name = "pyvko"
version = "0.1.11"
description = "VK API utilities"
version = "0.1.0"
description = "VK API typed wrapper library"
readme = "README.md"
authors = [
{ name = "Harry Djachenko", email = "i.s.djachenko@gmail.com" }
]
license = { text = "MIT" }
classifiers = [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
authors = [{ name = "Igor Djachenko" }]
requires-python = ">=3.10"

[project.optional-dependencies]
test = [
"pytest",
"ruff",
"mypy",
]
keywords = ["vk", "api", "social"]
dependencies = [
"vk_api",
"requests>=2.25.0",
release = [
"build",
"python-semantic-release",
]

[project.urls]
Homepage = "https://github.com/yourusername/pyvko"
"Bug Tracker" = "https://github.com/yourusername/pyvko/issues"
"Source Code" = "https://github.com/yourusername/pyvko"
Homepage = "https://github.com/djachenko/pyvko"
Repository = "https://github.com/djachenko/pyvko"
Issues = "https://github.com/djachenko/pyvko/issues"

[project.optional-dependencies]
dev = ["pytest>=8.0"]
[tool.setuptools.packages.find]
where = ["src"]

[[tool.mypy.overrides]]
module = "vk_api.*"
ignore_missing_imports = true

[tool.setuptools]
include-package-data = true
[[tool.mypy.overrides]]
module = "pyvko.pyvko_runner"
ignore_errors = true

[tool.pytest.ini_options]
testpaths = ["tests"]
markers = [
"smoke: базовая связность, read-only",
"create: создаёт ресурс в VK",
"destructive: удаляет ресурс в VK",
]
[tool.semantic_release]
version_toml = ["pyproject.toml:project.version"]
branch = "master"
build_command = "pip install build && python -m build"
changelog_file = "CHANGELOG.md"
commit_message = "chore(release): v{version} [no ci]"
major_on_zero = false
allow_zero_version = true
File renamed without changes.
14 changes: 11 additions & 3 deletions pyvko/api_based.py → src/pyvko/api_based.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


class RequestRoot:
def get_request(self, parameters: Dict = None):
def get_request(self, parameters: Dict | None = None):
return parameters or {}


Expand All @@ -15,8 +15,12 @@ class ApiMixin(RequestRoot, ABC):
def new_api(self) -> VkApi:
pass

@property
def api(self) -> Any:
assert False, f"{type(self).__name__} still uses unmigrated api — switch to new_api"

@abstractmethod
def get_request(self, parameters: Dict = None) -> Dict:
def get_request(self, parameters: Dict | None = None) -> Dict:
return super().get_request(parameters)


Expand All @@ -32,13 +36,17 @@ def __init__(self, api: Any) -> None:
def new_api(self) -> Any:
return self.__api

@property
def api(self) -> Any:
assert False, f"{type(self).__name__} still uses unmigrated api — switch to new_api"

@staticmethod
def __get_default_object():
return {
# "v": ApiBased.__VERSION,
}

def get_request(self, parameters: Dict = None) -> Dict:
def get_request(self, parameters: Dict | None = None) -> Dict:
if parameters is None:
parameters = {}

Expand Down
File renamed without changes.
7 changes: 3 additions & 4 deletions pyvko/aspects/albums.py → src/pyvko/aspects/albums.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from pathlib import Path
from typing import Any, Dict, List

from vk_api import VkApi

from pyvko.api_based import ApiMixin, ApiBased
from pyvko.attachment.attachment import Attachment
Expand Down Expand Up @@ -40,7 +39,7 @@ def get_photos(self) -> List[Photo]:

return photos

def get_request(self, parameters: Dict = None) -> dict:
def get_request(self, parameters: Dict | None = None) -> dict:
if parameters is None:
parameters = {}

Expand Down Expand Up @@ -86,7 +85,7 @@ class Albums(ApiMixin, ABC):
def id(self) -> int:
pass

def __get_albums(self, parameters: Dict = None) -> List[Album]:
def __get_albums(self, parameters: Dict | None = None) -> List[Album]:
request = self.__get_owned_request(parameters)

result = self.api.photos.getAlbums(**request)
Expand Down Expand Up @@ -125,7 +124,7 @@ def create_album(self, name: str) -> Album:

return created_album

def __get_owned_request(self, parameters: Dict = None) -> dict:
def __get_owned_request(self, parameters: Dict | None = None) -> dict:
if parameters is None:
parameters = {}
else:
Expand Down
10 changes: 4 additions & 6 deletions pyvko/aspects/comments.py → src/pyvko/aspects/comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
from datetime import datetime
from typing import List, Dict, Any

API = Any

from pyvko.api_based import ApiMixin, ApiBased
from pyvko.attachment.attachment import Attachment
from pyvko.attachment.attachment_parser import AttachmentParser
Expand All @@ -17,7 +15,7 @@ class CommentModel:
text: str
from_group: int = 0

attachments: List[Attachment] = None
attachments: List[Attachment] | None = None

def to_request(self):
request = {}
Expand Down Expand Up @@ -61,12 +59,12 @@ def date(self) -> datetime:

def __init__(
self,
api: API,
api: Any,
comment_id: int,
owner_id: int,
date: datetime,
text: str,
attachments: List[Attachment]
attachments: List[Attachment] | None
) -> None:
super().__init__(api)

Expand All @@ -77,7 +75,7 @@ def __init__(
self.__attachments = attachments

@classmethod
def from_api_object(cls, api_object: Dict, api: API) -> 'Comment':
def from_api_object(cls, api_object: Dict, api: Any) -> 'Comment':
if "attachments" in api_object:
parser = AttachmentParser.shared()

Expand Down
24 changes: 11 additions & 13 deletions pyvko/aspects/events.py → src/pyvko/aspects/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,29 +79,25 @@ def __init__(self, api: Any, event_object: Dict, settings_object: Dict | None) -
self.is_closed = bool(event_object["is_closed"])
self.url = event_object["screen_name"]

self.event_category: Event.Category | None

if settings_object is None:
self.event_category = None
self.__sections: Dict[Event.Section, Event.SectionState] = {}
self.main_section = None
self.secondary_section = None
self.organiser = None
else:
self.event_category: Event.Category | None = None
self.__sections: Dict[Event.Section, Event.SectionState] = {}
self.main_section: Event.Section | None = None
self.secondary_section: Event.Section | None = None
self.organiser: int | None = None

if settings_object is not None:
category_value = settings_object["public_category"]

if category_value != 0:
self.event_category = Event.Category(category_value)
else:
self.event_category = None

self.__sections: Dict[Event.Section, Event.SectionState] = {
self.__sections = {
s: Event.SectionState(settings_object[s.value]) for s in Event.Section
}

self.main_section = Event.Section.from_index(settings_object["main_section"])
self.secondary_section = Event.Section.from_index(settings_object["secondary_section"])
self.organiser: int | None = settings_object.get("event_object_id")
self.organiser = settings_object.get("event_object_id")

@property
def id(self) -> int:
Expand Down Expand Up @@ -165,6 +161,8 @@ def create_event(self, title: str) -> Event:

event = self.get_event(response["id"])

assert event is not None

return event

def get_event(self, url: str | int) -> Event | None:
Expand Down
9 changes: 4 additions & 5 deletions pyvko/aspects/groups.py → src/pyvko/aspects/groups.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABC
from typing import Dict, List, Any, TYPE_CHECKING
from typing import Any, Dict, List, TYPE_CHECKING

if TYPE_CHECKING:
from pyvko.entities.user import User
Expand All @@ -8,6 +8,7 @@
from pyvko.aspects.albums import Albums
from pyvko.aspects.events import Events, Event
from pyvko.aspects.posts import Posts
from pyvko.entities.user import User
from pyvko.shared.utils import get_all


Expand Down Expand Up @@ -43,8 +44,6 @@ def url(self) -> str:
return self.__url

def get_members(self) -> List['User']:
from pyvko.entities.user import User

parameters = {
"group_id": self.id,
"sort": "time_desc",
Expand All @@ -55,9 +54,9 @@ def get_members(self) -> List['User']:

parameters = self.get_request(parameters)

users_descriptions = get_all(parameters, self.api.groups.getMembers)
users_descriptions = get_all(parameters, self.new_api.groups.getMembers)

users = [User(api=self.api, user_object=description) for description in users_descriptions]
users = [User(api=self.new_api, user_object=description) for description in users_descriptions]

return users

Expand Down
Loading
Loading