Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding 3 more functions to svn: propdel(), propget() and propset() and some smaller changes #166

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
112 changes: 112 additions & 0 deletions dev/test2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env python2.7

from __future__ import print_function # PY3

import sys
import os
import os.path
from pprint import pprint

import svn
from svn.exception import SvnException
from svn.test_support import temp_repo, temp_checkout, populate_prop_files


def cont(conttext='continue'):
try:
ctext=raw_input(' ENTER to %s:' % conttext)
except: # PY3
ctext=input(' ENTER to %s:' % conttext)
return ctext


tempdir = os.environ.get('TEMPDIR',os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../temp')))

dev_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, dev_path)

import logging
_logger = logging.getLogger()
_logger.setLevel(logging.DEBUG)
logging.getLogger('boto').setLevel(logging.INFO)

ch = logging.StreamHandler()

FMT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
formatter = logging.Formatter(FMT)
ch.setFormatter(formatter)
_logger.addHandler(ch)


with temp_repo() as (repo_path, _):
print('Temp repo created at %s' % repo_path)

with temp_checkout() as (wcpath, lc):
print('Working Copy created at %s' % wcpath)

populate_prop_files()
lc.commit("Second revision.")

lc.propset('svn:mime-type','image/jpeg', rel_path='foo.jpg')
lc.propset('owner','sally', rel_path='foo.bar')
lc.propset('svn:ignore','foo.bak') # set on directory level

print('\n--- info for foo.bar ---')
pprint(lc.info('foo.bar'))

pprint(lc.properties('foo.bar'))

print('\n--- info for foo.jpg ---')
pprint(lc.info('foo.jpg'))
pprint(lc.properties('foo.jpg'))

cont('before committing properties')
lc.commit('Committing properties')
lc.update()

print('\npropget for foo.jpg for svn:mime-type:', lc.propget('svn:mime-type', rel_path='foo.jpg'))

print('\n--- info for foo.bar after setting properties owner svn:ignore & commit---')
pprint(lc.info('foo.bar'))
cont()

lc.propset('svn:keywords','Author Date Rev', rel_path='foo.bar')

print('\n--- info and properties for foo.bar ---')
pprint(lc.info('foo.bar'))
pprint(lc.properties(rel_path='foo.bar'))

lc.propdel('owner', rel_path='foo.bar')

lc.commit('Committing after deleting property')
lc.update()
cont()

print('\nget property svn:ignore on . =', lc.propget('svn:ignore','.') )

print('--- properties for foo.bar HEAD ---')
pprint(lc.properties(rel_path='foo.bar'))

print('info on foo.bar')
pprint(lc.info(rel_path='foo.bar'))

print('\n--- properties for foo.bar rev 1 ---')
pprint(lc.properties(rel_path='foo.bar',revision=1))
print('properties for foo.bar rev 2:')
pprint(lc.properties(rel_path='foo.bar',revision=2))
print('\n--- properties for foo.bar rev 3 ---')
pprint(lc.properties(rel_path='foo.bar',revision=3))

#with self.assertRaises(Exception): # svn.exception.SvnException):
# lc.propget('owner', rel_path='foo.bar')
# self.assertRaises(Exception, lc.propget,'owner', rel_path='foo.bar')

try:
lc.propget('owner', rel_path='foo.bar')
except SvnException as sx:
pass

print(lc.properties(rel_path='foo.bar'))

cont('end of script')

138 changes: 126 additions & 12 deletions svn/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,29 @@
_HUNK_HEADER_RIGHT_PREFIX = '+++ '
_HUNK_HEADER_LINE_NUMBERS_PREFIX = '@@ '

def normpath2(url_or_path):
""" Normalize url or path on Windows
convert to lower case and replace backslash by slash
This avoids path strings to change during processing makeing them difficult to compare:
- backslash appears as 2 backslashes or as %5C
- drive letter upper or lower case
"""
import sys
if not sys.platform.startswith('win'):
return url_or_path
else:
up = url_or_path.replace('\\','/')
up = up.replace('%5C','/')
if len(up) < 3:
return up
elif up[1]==':':
return up[0:1].upper()+up[1:]
elif len(up) < 10:
return up
elif up.startswith('file:///') and up[9]==':':
return up[:8]+up[8:9].upper()+up[9:]
else:
return up

class CommonClient(svn.common_base.CommonBase):
def __init__(self, url_or_path, type_, username=None, password=None,
Expand Down Expand Up @@ -133,45 +156,134 @@ def info(self, rel_path=None, revision=None):

return info

def properties(self, rel_path=None):
def properties(self, rel_path=None, revision=None):
""" Return a dictionary with all svn-properties associated with a
relative path.
:param rel_path: relative path in the svn repo to query the
properties from
:param revision: revision number (default working copy)
:returns: a dictionary with the property name as key and the content
as value
"""
cmd = []
if revision is not None:
cmd += ['-r', str(revision)]

full_url_or_path = self.__url_or_path
if rel_path is not None:
full_url_or_path += '/' + rel_path
cmd += ['--xml', full_url_or_path]

result = self.run_command(
'proplist',
['--xml', full_url_or_path],
cmd,
do_combine=True)

# query the proper list of this path
root = xml.etree.ElementTree.fromstring(result)
target_elem = root.find('target')
property_names = [p.attrib["name"]
for p in target_elem.findall('property')]

if target_elem is not None:
property_names = [p.attrib["name"]
for p in target_elem.findall('property')]
else: # no properties found
property_names = []

# now query the content of each propery
property_dict = {}

for property_name in property_names:
cmd = []
if revision is not None:
cmd += ['-r', str(revision)]
cmd += ['--xml', property_name, full_url_or_path, ]

result = self.run_command(
'propget',
['--xml', property_name, full_url_or_path, ],
cmd,
do_combine=True)

root = xml.etree.ElementTree.fromstring(result)
target_elem = root.find('target')
property_elem = target_elem.find('property')
property_dict[property_name] = property_elem.text

return property_dict

def propdel(self, property_name, rel_path=None, revision=None):
""" Delete a property with property_anme for the
url_or_path

:param rel_path: relative path in the svn repo to query the
properties from
:param revision: revision number (default working copy)

"""

cmd = [property_name,]

full_url_or_path = self.__url_or_path

if rel_path is not None:
full_url_or_path += '/' + rel_path

if revision is not None:
cmd += ['-r', str(revision)]

cmd.append(full_url_or_path)

self.run_command('propdel', cmd)

def propget(self, property_name, rel_path=None, revision=None):
""" Return a dictionary with the url_or_path as key and the
text for the property_name as value

:param rel_path: relative path in the svn repo to query the
properties from
:param revision: revision number (default working copy)
:returns: a dictionary with the url_or_path as key and the content
of the property as value
"""
cmd = []
if revision is not None:
cmd += ['-r', str(revision)]

full_url_or_path = self.__url_or_path
if rel_path is not None:
full_url_or_path += '/' + rel_path
cmd += ['--xml', property_name, full_url_or_path, ]

result = self.run_command(
'propget',
cmd,
do_combine=True)
root = xml.etree.ElementTree.fromstring(result)
target_elem = root.find('target')
property_elem = target_elem.find('property')

return { normpath2(full_url_or_path): property_elem.text.strip('\n') }

def propset(self, property_name, property_value, rel_path=None, revision=None):
""" Set the property_name to the property_value
for the url_or_path as key
:param property_name: name of the property
:param property_value: value of the property to be set
:param rel_path: relative path in the svn repo to query the
properties from
:param revision: revision number (default working copy)
"""
cmd = [property_name, property_value]

if revision is not None:
cmd += ['-r', str(revision)]

full_url_or_path = self.__url_or_path
if rel_path is not None:
full_url_or_path += '/' + rel_path
cmd.append(full_url_or_path)

self.run_command('propset', cmd)

def cat(self, rel_filepath, revision=None):
cmd = []
if revision is not None:
Expand Down Expand Up @@ -303,7 +415,7 @@ def list(self, extended=False, rel_path=None):
'ls',
['--xml', full_url_or_path],
do_combine=True)

root = xml.etree.ElementTree.fromstring(raw)

list_ = root.findall('list/entry')
Expand All @@ -312,7 +424,6 @@ def list(self, extended=False, rel_path=None):

kind = entry_attr['kind']
name = entry.find('name').text

size = entry.find('size')

# This will be None for directories.
Expand Down Expand Up @@ -404,14 +515,14 @@ def diff_summary(self, old, new, rel_path=None):
diff = []
for element in root.findall('paths/path'):
diff.append({
'path': element.text,
'path': normpath2(element.text),
'item': element.attrib['item'],
'kind': element.attrib['kind'],
})

return diff

def diff(self, old, new, rel_path=None):
def diff(self, old, new, rel_path=None, raw=False):
"""Provides output of a diff between two revisions (file, change type,
file type)
"""
Expand All @@ -431,6 +542,9 @@ def diff(self, old, new, rel_path=None):
do_combine=True)

diff_result = diff_result.strip()

if raw:
return diff_result

# Split the hunks.

Expand Down Expand Up @@ -484,7 +598,7 @@ def _split_file_hunk(self, file_hunk):

# Index: /tmp/testsvnwc/bb
# ===================================================================
filepath = lines[0][len(_FILE_HUNK_PREFIX):]
filepath = normpath2(lines[0][len(_FILE_HUNK_PREFIX):])

# File was added. We have the file-hunk header but no actual hunks.
if len(lines) == 3:
Expand Down Expand Up @@ -559,8 +673,8 @@ def _split_file_hunk(self, file_hunk):
})

hunks_info = {
'left_phrase': file_hunk_left_phrase,
'right_phrase': file_hunk_right_phrase,
'left_phrase': [normpath2(file_hunk_left_phrase[0]), file_hunk_left_phrase[1]],
'right_phrase':[normpath2(file_hunk_right_phrase[0]), file_hunk_right_phrase[1]],
'hunks': hunks,
}

Expand Down
2 changes: 1 addition & 1 deletion svn/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def status(self, rel_path=None):
revision = int(revision)

yield _STATUS_ENTRY(
name=name,
name=svn.common.normpath2(name),
type_raw_name=change_type_raw,
type=change_type,
revision=revision
Expand Down
Loading