Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
KOLANICH committed Dec 21, 2022
0 parents commit 8877ad9
Show file tree
Hide file tree
Showing 14 changed files with 530 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true

[*]
charset = utf-8
indent_style = tab
indent_size = 4
insert_final_newline = true
end_of_line = lf

[*.{yml,yaml}]
indent_style = space
indent_size = 2
1 change: 1 addition & 0 deletions .github/.templateMarker
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
KOLANICH/python_project_boilerplate.py
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
allow:
- dependency-type: "all"
15 changes: 15 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
build:
runs-on: ubuntu-22.04
steps:
- name: typical python workflow
uses: KOLANICH-GHActions/typical-python-workflow@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
__pycache__
*.pyc
*.pyo
/*.egg-info
/build
/dist
/.eggs
/monkeytype.sqlite3
/.mypy_cache
*.py,cover
/.coverage
/rspec.xml
60 changes: 60 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#image: pypy:latest
image: registry.gitlab.com/kolanich-subgroups/docker-images/fixed_python:latest
stages:
- dependencies
- build
- trigger
- test

variables:
DOCKER_DRIVER: overlay2
SAST_ANALYZER_IMAGE_TAG: latest
SAST_DISABLE_DIND: "true"
SAST_CONFIDENCE_LEVEL: 5
CODECLIMATE_VERSION: latest

include:
- template: SAST.gitlab-ci.yml
- template: Code-Quality.gitlab-ci.yml

build:
tags:
- shared
stage: build
variables:
GIT_DEPTH: "1"
PYTHONUSERBASE: ${CI_PROJECT_DIR}/python_user_packages

before_script:
- export PYTHON_MODULES_DIR=${PYTHONUSERBASE}/lib/python3.7
- export EXECUTABLE_DEPENDENCIES_DIR=${PYTHONUSERBASE}/bin
- export PATH="$PATH:$EXECUTABLE_DEPENDENCIES_DIR" # don't move into `variables` any of them, it is unordered
script:
- mkdir ./wheels
- python3 ./setup.py bdist_wheel
- mv ./dist/*.whl ./wheels/AnyVer-0.CI_python-py3-none-any.whl
- pip3 install --upgrade --pre --user ./wheels/AnyVer-0.CI_python-py3-none-any.whl
- coverage run --source=AnyVer --branch -m pytest --junitxml=./rspec.xml ./tests/tests.py
- coverage report -m
- coverage xml

coverage: /^TOTAL\\s+.+?(\\d{1,3}%)$/

cache:
paths:
- $PYTHONUSERBASE

artifacts:
paths:
- wheels
reports:
junit: ./rspec.xml
cobertura: ./coverage.xml

update_prebuilder_dependencies_docker_image:
only:
- master
stage: trigger
allow_failure: true
trigger:
project: KOLANICH-subgroups/docker-images/prebuilder_dependencies
191 changes: 191 additions & 0 deletions AnyVer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
__all__ = ("AnyVer",)

"""This module is meant to allow you to parse, compare and edit an any version string containing numbers in the order `major version` `minor version` `patch version`. Major version can be a year.
"""

import typing
import re
from array import array

defaultSeparator = "."
defaultSuffixFormat = "-{hash}"
specifiers2Try = "BHLQ"


class _AnyVer:
"""This class contains the functionality to store and compare version numbers."""

__slots__ = ("format", "parsed", "hash", "suffixFormat")

def __init__(self, parsed: typing.Iterable[int], hash: typing.Optional[str] = None, *, format=None, suffixFormat="") -> None: # pylint: disable=W0622
for s in specifiers2Try:
try:
parsed = array(s, parsed)
break
except OverflowError:
continue
self.parsed = parsed
self.hash = hash
if format is None:
format = defaultSeparator.join("{" + str(i) + "}" for i in range(len(parsed)))
self.format = format

if not suffixFormat and hash:
suffixFormat = defaultSuffixFormat
self.suffixFormat = suffixFormat

def __iter__(self) -> typing.Iterator[int]:
yield from self.parsed

def __len__(self) -> int:
return len(self.parsed)

def __getitem__(self, k: int) -> int:
return self.parsed[k]

def __setitem__(self, k: int, v: int) -> None:
self.parsed[k] = v

def __repr__(self) -> str:
return self.__class__.__name__ + "(" + ", ".join((repr(self.parsed), repr(self.hash), "format=" + repr(self.format), "suffixFormat=" + repr(self.suffixFormat))) + ")"

def __str__(self) -> str:
return self.format.format(*self.parsed) + self.suffixFormat.format(hash=self.hash)

def __eq__(self, other: typing.Union["_AnyVer", typing.Iterable[int]]) -> bool:
if not self.format == other.format:
print(repr(self.format), repr(other.format))
return tuple(self) == tuple(other) and ((self.format == other.format) if isinstance(other, __class__) else True)

def __hash__(self):
return hash(tuple(self) + (self.format,))

def __gt__(self, other) -> bool:
return tuple(self) > tuple(other)

def __lt__(self, other) -> bool:
return tuple(self) < tuple(other)

def __radd__(self, other: str) -> str:
if isinstance(other, str):
return other + str(self)
raise NotImplementedError()
if isinstance(other, _AnyVer):
return _AnyVer()

def __add__(self, other: str) -> str:
if isinstance(other, str):
return str(self) + other
raise NotImplementedError()
if isinstance(other, _AnyVer):
return _AnyVer()

@property
def major(self) -> typing.Optional[int]:
if len(self) < 1:
raise AttributeError()
return self.parsed[0]

@major.setter
def major(self, v) -> None:
if len(self) < 1:
raise AttributeError()
self.parsed[0] = v

@property
def minor(self) -> typing.Optional[int]:
if len(self) < 2:
raise AttributeError()
return self.parsed[1]

@minor.setter
def minor(self, v) -> None:
if len(self) < 2:
raise AttributeError()
self.parsed[1] = v

@property
def patch(self) -> typing.Optional[int]:
if len(self) < 3:
raise AttributeError()
return self.parsed[2]

@patch.setter
def patch(self, v) -> None:
if len(self) < 3:
raise AttributeError()
self.parsed[2] = v

@property
def tweak(self) -> typing.Optional[int]:
if len(self) < 4:
raise AttributeError()
return self.parsed[3]

@tweak.setter
def tweak(self, v) -> None:
if len(self) < 4:
raise AttributeError()
self.parsed[3] = v


numberRx = re.compile("(\\b|(?<=\\w))\\d+(\\b|(?=\\w))", re.I)
hexRx = re.compile("\\b([\\da-f]+[a-f][\\da-f]*|[\\da-f]*[a-f][\\da-f]+)\\b", re.I)


def parseVersionComponents(ver: str) -> typing.Dict[str, typing.Union[typing.Optional[str], typing.List[int]]]:
ver1 = ver.replace("}", "}}").replace("{", "{{")

hash = "" # pylint: disable=W0622
maxPos = 0
def replacerHash(m):
nonlocal hash, maxPos
hash = m.group(0)[::-1] # pylint: disable=W0622
maxPos = len(ver1) - m.span()[1]
return "{hash}"[::-1]

ver2 = hexRx.sub(replacerHash, ver1[::-1])[::-1]
if maxPos:
maxPos += len(ver2) - len(ver1)
suffix = ver2[maxPos:]
ver2 = ver2[:maxPos] # pylint: disable=W0622
else:
ver2 = ver1
suffix = ""


parsed = []
maxPos = 0
def replacerVersion(m):
nonlocal parsed, maxPos
idx = len(parsed)
maxPos = max(m.span()[1], maxPos)
parsed.append(int(m.group(0)))
return "{" + str(idx) + "}"

rr = numberRx.subn(replacerVersion, ver2)
if not rr[1]:
if not hash:
raise ValueError("Version is not parsed: " + repr(ver))
ver3 = rr[0]
maxPos += len(ver3) - len(ver2)

format = ver3[:maxPos] # pylint: disable=W0622
suffix = ver3[maxPos:] + suffix
res = {"format": format, "parsed": parsed, "hash": hash, "suffixFormat": suffix}
return res


class AnyVer(_AnyVer):
__slots__ = ()

def __init__(self, ver: str) -> None:
if isinstance(ver, _AnyVer):
super().__init__(ver.parsed, ver.hash, format=ver.format, suffixFormat=ver.suffixFormat)
elif isinstance(ver, str):
super().__init__(**parseVersionComponents(ver))
else:
raise ValueError("Incorrect type for `ver` argument: " + repr(type(ver)))

def __repr__(self) -> str:
return self.__class__.__name__ + "(" + repr(str(self)) + ")"
1 change: 1 addition & 0 deletions Code_Of_Conduct.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
No codes of conduct!
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include UNLICENSE
include *.md
include tests
include .editorconfig
19 changes: 19 additions & 0 deletions ReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
AnyVer.py [![Unlicensed work](https://raw.githubusercontent.com/unlicense/unlicense.org/master/static/favicon.png)](https://unlicense.org/)
=========
![GitLab Build Status](https://gitlab.com/KOLANICH/AnyVer.py/badges/master/pipeline.svg)
[wheel (GHA via `nightly.link`)](https://nightly.link/prebuilder/AnyVer.py/workflows/CI/master/AnyVer-0.CI-py3-none-any.whl)
![GitLab Coverage](https://gitlab.com/prebuilder/AnyVer.py/badges/master/coverage.svg)
[![Coveralls Coverage](https://img.shields.io/coveralls/prebuilder/AnyVer.py.svg)](https://coveralls.io/r/prebuilder/AnyVer.py)
[![GitHub Actions](https://github.com/prebuilder/AnyVer.py/workflows/CI/badge.svg)](https://github.com/prebuilder/AnyVer.py/actions/)
[![Libraries.io Status](https://img.shields.io/librariesio/github/prebuilder/AnyVer.py.svg)](https://libraries.io/github/prebuilder/AnyVer.py)
[![Code style: antiflash](https://img.shields.io/badge/code%20style-antiflash-FFF.svg)](https://github.com/KOLANICH-tools/antiflash.py)

Just a version parsing library.

Extracts numbers, their formats and a hex-number (available as `hash`) from version strings. Allows comparisons (`>`, `<` and `==`) and editing. Allows accessing components separately using numeric indices. Allows iteration. First 4 components are available as properties `major`, `minor`, `patch` and `tweak`.

See [`tests/tests.py`](./tests/tests.py) for the examples.

Requirements
------------
* [`Python 3`](https://www.python.org/downloads/).
24 changes: 24 additions & 0 deletions UNLICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <https://unlicense.org/>
37 changes: 37 additions & 0 deletions logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 8877ad9

Please sign in to comment.