diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..e7818a5 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,33 @@ +[[package]] +name = "python-dateutil" +version = "2.6.1" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "six" +version = "1.11.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = "*" + +[metadata] +lock-version = "1.1" +python-versions = ">=2.7.18" +content-hash = "6bc126a04fb1701e58134be5a0900b5f3a4a266c44b274797f59eff1357f2c92" + +[metadata.files] +python-dateutil = [ + {file = "python-dateutil-2.6.1.tar.gz", hash = "sha256:891c38b2a02f5bb1be3e4793866c8df49c7d19baabf9c1bad62547e0b4866aca"}, + {file = "python_dateutil-2.6.1-py2.py3-none-any.whl", hash = "sha256:95511bae634d69bc7329ba55e646499a842bc4ec342ad54a8cdb65645a0aad3c"}, +] +six = [ + {file = "six-1.11.0-py2.py3-none-any.whl", hash = "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"}, + {file = "six-1.11.0.tar.gz", hash = "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9"}, +] diff --git a/publish.cmd b/publish.cmd new file mode 100644 index 0000000..ea3957c --- /dev/null +++ b/publish.cmd @@ -0,0 +1,13 @@ + +@echo off +set PYPI_REPO_NAME=artifactory_upload + + +choice /C YN /m "Did you change the version?" +if ["%errorlevel%"]==["2"] exit /b + +echo Publishing +poetry publish --repository %PYPI_REPO_NAME% --build %* + +echo -i https://artifactory.wgdp.io/artifactory/api/pypi/wotk-pypi-wotdevtool/simple/ --extra-index-url https://artifactory.wgdp.io/artifactory/api/pypi/wotd-pypi/simple/ --extra-index-url https://pypi.python.org/simple +echo if this fails - activate python 3.12 venv first diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..805194f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,26 @@ +[tool.poetry] +name = "PySVN" +version = "1.19.21" +description = "The pythonic interface to Subversion. Fork." +authors = [] +maintainers = ["Aleksei Rubashev "] +packages = [ + { include = "svn" }, +] + +[tool.poetry.dependencies] +python = ">=2.7.18" +python-dateutil = ">=2.2" + +[[tool.poetry.source]] +name = "artifactory_upload" +url = "https://artifactory.wgdp.io/artifactory/api/pypi/wotk-pypi-wotdevtool/simple" +priority = "primary" + +[poetry.group.dev.dependencies] +poetry = "*" # +pytest = ">=4.6" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/svn/common.py b/svn/common.py index a53e2f2..af34eae 100644 --- a/svn/common.py +++ b/svn/common.py @@ -1,14 +1,13 @@ import collections import logging import os -import subprocess import xml.etree.ElementTree import dateutil.parser +from svn.common_base import CommonBase import svn.constants import svn.exception -import svn.common_base _LOGGER = logging.getLogger(__name__) @@ -19,7 +18,7 @@ _HUNK_HEADER_LINE_NUMBERS_PREFIX = '@@ ' -class CommonClient(svn.common_base.CommonBase): +class CommonClient(CommonBase): def __init__(self, url_or_path, type_, username=None, password=None, svn_filepath='svn', trust_cert=None, env={}, *args, **kwargs): super(CommonClient, self).__init__(*args, **kwargs) @@ -37,10 +36,10 @@ def __init__(self, url_or_path, type_, username=None, password=None, self.__type = type_ def run_command(self, subcommand, args, **kwargs): - cmd = [self.__svn_filepath, '--non-interactive'] + cmd = [self.__svn_filepath] if self.__trust_cert: - cmd += ['--trust-server-cert'] + cmd += ['--trust-server-cert', '--non-interactive'] if self.__username is not None and self.__password is not None: cmd += ['--username', self.__username] @@ -285,6 +284,45 @@ def export(self, to_path, revision=None, force=False): self.run_command('export', cmd) + def status(self, rel_path=None, include_changelists=False): + full_url_or_path = self.__url_or_path + if rel_path is not None: + full_url_or_path += '/' + rel_path + + raw = self.run_command( + 'status', + ['--xml', full_url_or_path], + do_combine=True) + + root = xml.etree.ElementTree.fromstring(raw) + + list_ = root.findall('target/entry') + if include_changelists is True: + list_ += root.findall('changelist/entry') + + for entry in list_: + entry_attr = entry.attrib + name = entry_attr['path'] + + wcstatus = entry.find('wc-status') + wcstatus_attr = wcstatus.attrib + + change_type_raw = wcstatus_attr['item'] + change_type = svn.constants.STATUS_TYPE_LOOKUP[change_type_raw] + + # This will be absent if the file is "unversioned". It'll be "-1" + # if added but not committed. + revision = wcstatus_attr.get('revision') + if revision is not None: + revision = int(revision) + + yield svn.constants._STATUS_ENTRY( + name=name, + type_raw_name=change_type_raw, + type=change_type, + revision=revision + ) + def list(self, extended=False, rel_path=None): full_url_or_path = self.__url_or_path if rel_path is not None: diff --git a/svn/constants.py b/svn/constants.py index 7bfee62..213a11f 100644 --- a/svn/constants.py +++ b/svn/constants.py @@ -1,3 +1,15 @@ +import collections + +_STATUS_ENTRY = \ + collections.namedtuple( + '_STATUS_ENTRY', [ + 'name', + 'type_raw_name', + 'type', + 'revision', + ]) + + # Kinds K_DIR = 'dir' diff --git a/svn/local.py b/svn/local.py index 686063b..9e98a5b 100644 --- a/svn/local.py +++ b/svn/local.py @@ -1,24 +1,15 @@ import os -import collections import xml.etree import svn.constants -import svn.common +from svn.common import CommonClient -_STATUS_ENTRY = \ - collections.namedtuple( - '_STATUS_ENTRY', [ - 'name', - 'type_raw_name', - 'type', - 'revision', - ]) -class LocalClient(svn.common.CommonClient): - def __init__(self, path_, *args, **kwargs): - if os.path.exists(path_) is False: +class LocalClient(CommonClient): + def __init__(self, path_, allow_nonexistent=True, *args, **kwargs): + if not allow_nonexistent and os.path.exists(path_) is False: raise EnvironmentError("Path does not exist: %s" % path_) super(LocalClient, self).__init__( @@ -64,7 +55,7 @@ def cleanup(self): [], wd=self.path) - def status(self, rel_path=None): + def status(self, rel_path=None, include_changelists=False): path = self.path if rel_path is not None: path += '/' + rel_path @@ -77,6 +68,8 @@ def status(self, rel_path=None): root = xml.etree.ElementTree.fromstring(raw) list_ = root.findall('target/entry') + if include_changelists is True: + list_ += root.findall('changelist/entry') for entry in list_: entry_attr = entry.attrib name = entry_attr['path'] @@ -93,7 +86,7 @@ def status(self, rel_path=None): if revision is not None: revision = int(revision) - yield _STATUS_ENTRY( + yield svn.constants._STATUS_ENTRY( name=name, type_raw_name=change_type_raw, type=change_type,