Bazis Permit
An extension package for Bazis that provides a flexible and powerful permission management system with support for roles, permission groups, and granular access control at object and field levels.
Quick Start
# Install the package
uv add bazis-permit
# Create a model with permissions
from bazis.contrib.permit.models_abstract import PermitModelMixin, PermitSelectorMixin
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
from django.db import models
class Organization(PermitSelectorMixin, DtMixin, UuidMixin, JsonApiMixin):
"""Organization - selector source"""
name = models.CharField(max_length=255)
@classmethod
def get_selector_for_user(cls, user):
return user.organization
class Document(PermitModelMixin, DtMixin, UuidMixin, JsonApiMixin):
"""Document with access control"""
title = models.CharField(max_length=255)
org_owner = models.ForeignKey(Organization, on_delete=models.CASCADE)
# Create a route with permission checking
from bazis.contrib.permit.routes_abstract import PermitRouteBase
from django.apps import apps
class DocumentRouteSet(PermitRouteBase):
model = apps.get_model('myapp.Document')
Table of Contents
Description
Bazis Permit is an extension package for the Bazis framework that provides a comprehensive permission management system. The package includes:
- Role-based model — users have roles, roles contain permission groups
- Granular control — permissions at object and field levels
- Selectors — flexible system for linking permissions with objects
- Automatic checking — built-in permission verification in routes
- Dynamic schemas — JSON:API schemas adapt to user permissions
- Caching — efficient permission caching for improved performance
- Field filtering — visibility restrictions for fields based on permissions
This package requires installation of bazis and bazis-users packages.
Requirements
- Python: 3.12+
- bazis: latest version
- bazis-users: latest version
- PostgreSQL: 12+
- Redis: For caching
Installation
Using uv (recommended)
uv add bazis-permit
Using pip
pip install bazis-permit
Core Concepts
Permission System Levels
The permission system has the following hierarchy:
User
└─ role_current (Current role)
└─ Role
└─ groups_permission (Permission groups)
└─ GroupPermission
└─ permissions
└─ Permission
1. Permission
The simplest structure, represented as a string in a special format.
Model: bazis.contrib.permit.models.Permission
Examples:
entity.document.item.view.org_owner
entity.document.item.change.author
entity.document.field.view.all.description.enable
2. GroupPermission
A set of permissions grouped by some criterion.
Model: bazis.contrib.permit.models.GroupPermission
Example:
from bazis.contrib.permit.models import GroupPermission, Permission
# Create a permission group for document management
group = GroupPermission.objects.create(
slug='document_manager',
name='Document Manager'
)
# Add permissions to the group
group.permissions.add(
Permission.objects.get_or_create(slug='entity.document.item.add.all')[0],
Permission.objects.get_or_create(slug='entity.document.item.view.all')[0],
Permission.objects.get_or_create(slug='entity.document.item.change.author')[0],
)
3. Role
A role includes multiple permission groups.
Model: bazis.contrib.permit.models.Role
Example:
from bazis.contrib.permit.models import Role
# Create a role
role = Role.objects.create(
slug='manager',
name='Manager',
for_anonymous=False # Not available for anonymous users
)
# Add permission groups
role.groups_permission.add(
document_group,
report_group
)
4. User
A user can have multiple roles, but only one role is active.
Model: Must inherit from bazis.contrib.permit.models_abstract.UserPermitMixin
Example:
from bazis.contrib.permit.models_abstract import UserPermitMixin
from bazis.contrib.users.models_abstract import UserAbstract
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
class User(UserPermitMixin, UserAbstract, DtMixin, UuidMixin, JsonApiMixin):
class Meta:
verbose_name = 'User'
verbose_name_plural = 'Users'
# Assign a role to the user
user.roles.add(manager_role)
user.role_current = manager_role
user.save()
Permission Format
General permission format:
LABEL_APP.LABEL_MODEL.PERM_LEVEL.PERM_OPERATION.SELECTOR[.ADDITIONAL]+
Components:
LABEL_APP.LABEL_MODEL
Full model label including the app label.
Examples:
entity.documentorganization.organizationfacility.facility
PERM_LEVEL
Permission level:
- item — object level
- field — object field level (bounded context)
PERM_OPERATION
Operation to which the permission applies.
Basic operations (CrudAccessAction):
add— createview— read/viewchange— updatedelete— deletecheck— check (used for validation after create/update)
Custom operation example:
from bazis.core.schemas import AccessAction
class CustomAccessAction(AccessAction):
APPROVE = 'approve' # Approve
REJECT = 'reject' # Reject
SELECTOR
Name of a relation of the model that links the permission with an object: a foreign key,
a many-to-many field or a reverse relation (its related_name) to a selector source (see
Selectors), or a path of relations to one (parent__author).
Special selectors:
all— permission applies to all objectsself— the object is the selector source itself (the user; a source of several objects matches each of them)author— permission applies to objects where the user is the authororg_owner— permission applies to objects of the user's organizationparticipants— permission applies to objects where the user is one of the participants (a many-to-many field)
ADDITIONAL
Additional parameters to refine the permission.
Examples:
- Field conditions:
entity.document.item.view.author=__selector__&is_active=true
View documents where author = current user AND is_active = true
- Field-level restrictions:
entity.document.field.view.all.description.enable
Enable visibility of the description field for all
entity.document.field.view.author=__selector__&is_active=true.__all__.disable
Disable all fields for author's documents with is_active=true
- Value filters:
entity.document.field.add.all.name.filter:^[A-Z]+$
The name field must match the regex when creating
entity.parent.field.change.all.child_entities.filter:child_is_active=true
Only items with child_is_active=true can be added to child_entities
Related Objects
Three tools restrict the objects an item references through its relationships:
- The visibility of the related objects (Bazis 2.7, always on): a created or changed
item can reference only the objects the user can view (
viewpermission), and a reverse relation can link and unlink only the objects the user can change (changepermission). Other references fail with403and the error codeERR_RELATION_ACCESS. Only newly referenced objects are checked, so an unchanged relation to an object that became invisible does not block a change.includedshows only the objects the user can view. The core checks this withrestrict_querysetof the default route of the related model,PermitRouteBasefor a permission-protected model; a route turns it off withrelation_targets_check = False. A route without a user (not aUserRouteBase) is checked for the authenticated user of the request if it is known (UserMixin.CTX_USER_REQUEST), otherwise as for an anonymous user.BAZIS_PERMIT_RELATIONS_VIEW_CHECKandPermitRouteBase.relations_view_checkare deprecated and will be removed in 3.0: the setting has no effect,relations_view_check = Falseworks asrelation_targets_check = False. checkpermissions are verified on the saved item, whose selectors are filled at that point (also by triggers):entity.extended_entity.item.check.author_parentallows creating and changing extended entities only for the author of the parent entity.filter:field permissions restrict the objects a relation can reference (see above).- Filter, sorting and search (Bazis 2.9,
BAZIS_FILTERS_STRICT): thefilter,sortandsearchof a request reach only the fields the field permissions show the user (PermitRouteBase.query_fields), on the route and through a relation of another route into the objects of the model; the related objects are restricted byrestrict_queryset. A field aviewfield permission hides from the user in every object of the list is 400ERR_FILTER(as an unknown field), and a hidden search field is left out of the search. The scope is per user, not per object: a field shown in only some objects (a field permission with a selector such asauthor), or in an object that no field permission matches (such an object has every field), can be filtered, sorted and searched in all the objects the user sees. Hide such a field from the route (fields) if its values must not be found by trial. The conditions of the permissions themselves (selectors,filter:restrictions) are not restricted.
Permission Cache
The permissions of a role are cached for BAZIS_PERMISSION_CACHE_EXPIRE seconds (7 by
default). Any change of roles, permission groups, permissions or their relations
invalidates the cache of all roles.
An anonymous user matches no selector: a selector permission (e.g. view.author) of the
role for anonymous users allows nothing.
Roles in the Code
The roles and permission groups of an application are declared in its roles.py
module (the lists GROUPS and ROLES); migrate applies them:
# support/roles.py
from django.utils.translation import gettext_lazy as _
from bazis.contrib.permit.declare import Group, Role
CLIENT = Group('tickets_client', _('Tickets of the client'), [
'support.ticket.item.view.author',
'support.ticket.item.add.all',
])
GROUPS = [CLIENT]
ROLES = [Role('client', _('Client'), [CLIENT])]
- After
migrate(andflush), once all the migrations of the project are applied, the declared groups and roles exist and are markedmanaged, in one transaction. A secondmigratewrites nothing. - The permissions of a managed group are exactly the declared ones: a permission removed
from the code is revoked. The managed groups of a declared role are exactly the declared
ones: the groups that are not declared that the admin attached to the role stay, but a
managed group the admin attached to it is removed by the next
migrate. - Roles and groups that are not declared are left as they are. A declared slug that exists
unmarked is taken over (a warning in the log) and synced from then on: its permissions
that are not declared are revoked. A managed object no longer declared is kept:
permit.W006lists it; it is an ordinary object in the admin again (change or delete it). - Concurrent
migrateruns of a database apply the declarations one after the other (a PostgreSQL advisory lock). - The declarations are applied after the last migration: on a fresh database a data migration of the product runs before them and cannot rely on the declared roles.
- The names are English msgids (
gettext_lazy):name_enandname_ruget their translations from the catalogs of the project (locale/);permit.W004lists the names without a translation into a language ofLANGUAGES. - The admin shows declared objects read-only (a declared role still takes the groups of the admin) and does not delete them.
manage.py bazis_doctorchecks the declarations: the slugs, the groups of the roles, the model (aPermitModelMixin), the selector and the field of every permission (permit.E004,permit.E005), and from bazis 2.13 also compares them with the database it reaches (permit.W005,permit.W006); with an older coremanage.py check --database defaultdoes.
Selectors
Selectors allow linking permissions with specific objects through field values.
Creating a Selector Source
from bazis.contrib.permit.models_abstract import PermitSelectorMixin
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
from django.db import models
class Organization(PermitSelectorMixin, DtMixin, UuidMixin, JsonApiMixin):
"""Organization - selector source"""
name = models.CharField(max_length=255)
@classmethod
def get_selector_for_user(cls, user):
"""
Links user with organization.
Assumes User has an organization field.
"""
return user.organization
class Meta:
verbose_name = 'Organization'
verbose_name_plural = 'Organizations'
Using Selector in a Model
from bazis.contrib.permit.models_abstract import PermitModelMixin
class Document(PermitModelMixin, DtMixin, UuidMixin, JsonApiMixin):
"""Document with access control"""
title = models.CharField(max_length=255)
content = models.TextField()
org_owner = models.ForeignKey(
Organization,
verbose_name='Owner Organization',
on_delete=models.CASCADE
)
class Meta:
verbose_name = 'Document'
verbose_name_plural = 'Documents'
Creating a Permission with Selector
entity.document.item.view.org_owner
Meaning: User can view documents where org_owner equals user.organization
Many-to-many and Reverse Relations
A selector can also be a many-to-many field of the model or a reverse relation of a
selector source, named by its related_name. The object matches when the selector value
(the user, his organization...) is one of its related objects:
class Team(PermitModelMixin, DtMixin, UuidMixin, JsonApiMixin):
name = models.CharField(max_length=255)
class Meeting(PermitModelMixin, AuthorMixin, DtMixin, UuidMixin, JsonApiMixin):
title = models.CharField(max_length=255)
participants = models.ManyToManyField(User, related_name='meetings', blank=True)
team = models.ForeignKey(Team, null=True, on_delete=models.SET_NULL, related_name='meetings')
class User(UserPermitMixin, PermitSelectorMixin, UuidMixin, UserAbstract, JsonApiMixin):
team = models.ForeignKey(Team, null=True, on_delete=models.SET_NULL, related_name='members')
teams_watched = models.ManyToManyField(Team, related_name='watchers', blank=True)
entity.meeting.item.view.participants # many-to-many: the user is a participant
entity.team.item.view.members # reverse foreign key: the team of the user
entity.team.item.view.watchers # reverse many-to-many: the teams he watches
entity.meeting.item.view.team__members # a path: the meetings of the user's team
entity.meeting.item.change.participants
entity.meeting.item.check.participants # a change must keep the user a participant
entity.meeting.field.view.participants.description.disable
Such a selector is an EXISTS subquery (<selector>__pk=<value>): an object matches once,
whatever the number of its related objects, also combined with other permissions
(view.author and view.participants). It works for every operation, the field
permissions and the custom operations (the transits of bazis-statusy:
app.model.item.transit.participants.<status>.<transit>). The subquery uses the unique
index of the automatic through table (object_id, user_id) and the index of a foreign
key; a custom through model needs a unique constraint or an index on its two foreign
keys. autogen_selectors_fields is not needed for these selectors.
Usage
Creating Models
Selector Source Model
from bazis.contrib.permit.models_abstract import PermitSelectorMixin
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
from django.db import models
class Organization(PermitSelectorMixin, DtMixin, UuidMixin, JsonApiMixin):
name = models.CharField('Name', max_length=255)
@classmethod
def get_selector_for_user(cls, user):
return user.organization
class Meta:
verbose_name = 'Organization'
verbose_name_plural = 'Organizations'
Model with Access Control
from bazis.contrib.permit.models_abstract import PermitModelMixin
from bazis.contrib.author.models_abstract import AuthorMixin
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
from django.db import models
class ParentEntity(PermitModelMixin, AuthorMixin, DtMixin, UuidMixin, JsonApiMixin):
"""Parent entity with permissions"""
# Specify fields for automatic selector generation
autogen_selectors_fields = ['author']
name = models.CharField('Name', max_length=255)
description = models.TextField('Description', blank=True)
is_active = models.BooleanField('Active', default=True)
price = models.DecimalField('Price', max_digits=10, decimal_places=2)
class Meta:
verbose_name = 'Parent Entity'
verbose_name_plural = 'Parent Entities'
Selector Auto-generation:
autogen_selectors_fields — list of fields for which selector fields with GIN indexes will be automatically created. If None — auto-generation is disabled. Many-to-many and reverse relation selectors do not need them (see Many-to-many and Reverse Relations).
Creating Permissions
from bazis.contrib.permit.models import Permission, GroupPermission
# Permissions for ParentEntity
parent_entity_perms = [
'entity.parent_entity.item.add.all', # Everyone can create
'entity.parent_entity.item.view.author', # Author can view
'entity.parent_entity.item.change.author', # Author can edit
]
# Create permission group
group = GroupPermission.objects.create(
slug='parent_entity_group',
name='Parent Entity Permissions'
)
for perm_slug in parent_entity_perms:
perm, created = Permission.objects.get_or_create(slug=perm_slug)
group.permissions.add(perm)
Creating Roles
from bazis.contrib.permit.models import Role
# Create manager role
manager_role = Role.objects.create(
slug='manager',
name='Manager',
for_anonymous=False
)
# Add permission groups to role
manager_role.groups_permission.add(
parent_entity_group,
child_entity_group,
document_group
)
# Assign role to user
user.roles.add(manager_role)
user.role_current = manager_role
user.save()
Creating Routes
from bazis.contrib.permit.routes_abstract import PermitRouteBase
from bazis.contrib.author.routes_abstract import AuthorRouteBase
from bazis.core.schemas import SchemaFields
from django.apps import apps
class ParentEntityRouteSet(PermitRouteBase, AuthorRouteBase):
"""Route with automatic permission checking"""
model = apps.get_model('entity.ParentEntity')
fields = {
None: SchemaFields(
include={
'extended_entity': None,
'dependent_entities': None,
},
),
}
What PermitRouteBase provides:
- Automatic permission checking for every CRUD action
- Dynamic schemas — fields are automatically hidden/shown based on permissions
- QuerySet filtering — user sees only objects they have access to
- Meta-fields:
for_change— list of IDs available for editingfor_delete— list of IDs available for deletionfor_create— create availabilitycrud_actions— list of available actions for the object
Access Checking
Automatic Checking
In routes inheriting from PermitRouteBase, access checking happens automatically when accessing self.schemas.
Manual Checking in Custom Actions
from bazis.core.schemas import CrudAccessAction
from django.apps import apps
class DocumentRouteSet(PermitRouteBase):
model = apps.get_model('myapp.Document')
@http_post('/{item_id}/sign/')
def action_sign(self, item_id: str):
"""Sign document"""
# 404 if the user cannot view the document (as for a missing one)
document = self.set_item(item_id)
# Check: can user create signature
self.check_access(
CrudAccessAction.ADD,
apps.get_model('document.Signature')
)
# Signing logic
signature = Signature.objects.create(
document=document,
user=self.inject.user
)
return {'status': 'signed', 'signature_id': str(signature.id)}
Examples
Example 1: Basic Permission Setup
from bazis.contrib.permit.models import Permission, GroupPermission, Role
# Create permissions
permissions = {
'document_add': 'myapp.document.item.add.all',
'document_view_own': 'myapp.document.item.view.author',
'document_view_org': 'myapp.document.item.view.org_owner',
'document_change_own': 'myapp.document.item.change.author',
'document_delete_own': 'myapp.document.item.delete.author',
}
# Create group for regular users
user_group = GroupPermission.objects.create(
slug='document_user',
name='Document User'
)
for perm_slug in ['document_add', 'document_view_own', 'document_change_own']:
perm, _ = Permission.objects.get_or_create(slug=permissions[perm_slug])
user_group.permissions.add(perm)
# Create group for administrators
admin_group = GroupPermission.objects.create(
slug='document_admin',
name='Document Administrator'
)
for perm_slug in permissions.values():
perm, _ = Permission.objects.get_or_create(slug=perm_slug)
admin_group.permissions.add(perm)
# Create roles
user_role = Role.objects.create(slug='user', name='User')
user_role.groups_permission.add(user_group)
admin_role = Role.objects.create(slug='admin', name='Administrator')
admin_role.groups_permission.add(admin_group)
Example 2: Complex Permissions with Conditions
# Permissions with field conditions
complex_perms = [
# Can only create with name "Test name"
'entity.parent_entity.item.check.author=__selector__&name=Test name',
# Can view own objects
'entity.parent_entity.item.view.author=__selector__',
# Can edit only inactive own objects
'entity.parent_entity.item.change.author=__selector__&is_active=false',
# Description field visible only for active own objects
'entity.parent_entity.field.view.author=__selector__&is_active=true.description.enable',
# All other fields hidden for active own objects
'entity.parent_entity.field.view.author=__selector__&is_active=true.__all__.disable',
# Description field hidden for inactive own objects
'entity.parent_entity.field.view.author=__selector__&is_active=false.description.disable',
]
group = GroupPermission.objects.create(
slug='complex_permissions',
name='Complex Permissions'
)
for perm_slug in complex_perms:
perm, _ = Permission.objects.get_or_create(slug=perm_slug)
group.permissions.add(perm)
Example 3: Field Filters
# Field value filters
field_filter_perms = [
# child_name field must match regex when creating
'entity.child_entity.field.add.all.child_name.filter:^[A-Z]+$',
# Only active items can be added to child_entities
'entity.parent_entity.field.add.all.child_entities.filter:child_is_active=true',
# child_name must match regex when changing
'entity.child_entity.field.change.all.child_name.filter:^[A-Z]+$',
# Only active items can be added to child_entities when changing
'entity.parent_entity.field.change.all.child_entities.filter:child_is_active=true',
# Only active parents can be set in parent_entity
'entity.dependent_entity.field.add.all.parent_entity.filter:is_active=true',
'entity.dependent_entity.field.change.all.parent_entity.filter:is_active=true',
]
group = GroupPermission.objects.create(
slug='field_filters',
name='Field Filters'
)
for perm_slug in field_filter_perms:
perm, _ = Permission.objects.get_or_create(slug=perm_slug)
group.permissions.add(perm)
Example 4: Access Checking in Tests
import pytest
from bazis.contrib.permit.models import Permission, GroupPermission, Role
from bazis.contrib.users import get_user_model
User = get_user_model()
@pytest.mark.django_db
def test_permissions(sample_app):
# Create users
user_1 = User.objects.create_user('user1', email='user1@example.com', password='pass')
user_2 = User.objects.create_user('user2', email='user2@example.com', password='pass')
# Create permission group
group = GroupPermission.objects.create(slug='test_group', name='Test Group')
group.permissions.add(
Permission.objects.get_or_create(slug='entity.document.item.add.all')[0],
Permission.objects.get_or_create(slug='entity.document.item.view.author')[0],
Permission.objects.get_or_create(slug='entity.document.item.change.author')[0],
)
# Create role and assign to user
role = Role.objects.create(slug='user_role', name='User Role')
role.groups_permission.add(group)
user_1.roles.add(role)
user_1.role_current = role
user_1.save()
# user_1 can create documents
response = get_api_client(sample_app, user_1.jwt_build()).post(
'/api/v1/documents/document/',
json_data={
'data': {
'type': 'myapp.document',
'bs:action': 'add',
'attributes': {'title': 'New Document'}
}
}
)
assert response.status_code == 201
doc_id = response.json()['data']['id']
# user_1 can view their own document
response = get_api_client(sample_app, user_1.jwt_build()).get(
f'/api/v1/documents/document/{doc_id}/'
)
assert response.status_code == 200
# user_2 cannot view another user's document: it is not found for him
response = get_api_client(sample_app, user_2.jwt_build()).get(
f'/api/v1/documents/document/{doc_id}/'
)
assert response.status_code == 404
Example 5: Client Usage
class PermitClient {
constructor(apiUrl, token) {
this.apiUrl = apiUrl;
this.token = token;
}
async getSchemaForAction(resource, action, itemId = null) {
let url = `${this.apiUrl}/${resource}/`;
if (itemId) {
url += `${itemId}/schema_${action}/`;
} else {
url += `schema_${action}/`;
}
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${this.token}`
}
});
if (response.ok) {
return await response.json();
}
throw new Error('Cannot get schema');
}
async canPerformAction(resource, action, itemId = null) {
try {
await this.getSchemaForAction(resource, action, itemId);
return true;
} catch {
return false;
}
}
async getAvailableActions(resource, itemId) {
const response = await fetch(
`${this.apiUrl}/${resource}/${itemId}/`,
{
headers: {
'Authorization': `Bearer ${this.token}`
}
}
);
if (response.ok) {
const data = await response.json();
return data.meta.crud_actions || [];
}
return [];
}
}
// Usage
const client = new PermitClient('http://api.example.com/api/v1', jwtToken);
// Check if action is possible
const canEdit = await client.canPerformAction('documents/document', 'update', docId);
if (canEdit) {
console.log('User can edit this document');
}
// Get available actions
const actions = await client.getAvailableActions('documents/document', docId);
console.log('Available actions:', actions);
// ['view', 'change', 'delete']
License
Apache License 2.0
See LICENSE file for details.
Links
- Bazis Documentation — main repository
- Bazis Users — user management package
- Bazis Author — authorship tracking package
- Bazis Permit Repository — package repository
- Issue Tracker — report bugs or request features
Support
If you have questions or issues:
- Check the Bazis documentation
- Search existing issues
- Create a new issue with detailed information
Made with ❤️ by the Bazis team
Metadata
Release files for bazis-permit 2.9.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bazis_permit-2.9.1.tar.gz | 199.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bazis_permit-2.9.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 270.6 kB
Release files / bazis_permit-2.9.1.tar.gz
| Download URL | bazis_permit-2.9.1.tar.gz |
|---|---|
| Size | 199.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
18e2b5b9231d1b7223ec6cb322c3572ea17b300530fec3b4c64dd185ffb1639b
|
|
BLAKE2b-256 checksum How to use checksums |
ed09292298f62328efe32577986aa4a4fd23399705bf9779c965763f17a78fe6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.24 {"installer":{"name":"uv","version":"0.12.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / bazis_permit-2.9.1-py3-none-any.whl
| Download URL | bazis_permit-2.9.1-py3-none-any.whl |
|---|---|
| Size | 71.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
606da0aded5a3ce461e58868947b43ad430d1fa44c6763b7805f17ad7133b60a
|
|
BLAKE2b-256 checksum How to use checksums |
02a631921145b8b9c8b782bf2022dc912e48de77b2b66958d0ac77e51bfdc376
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.24 {"installer":{"name":"uv","version":"0.12.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|