use configparser from py3.5 so we can set delimiters to only equal char to fix #83
This commit is contained in:
parent
079b40c865
commit
50141ef7b9
6 changed files with 1652 additions and 5 deletions
7
tests/samples/configs/project_map_with_colon_in_key.cfg
Normal file
7
tests/samples/configs/project_map_with_colon_in_key.cfg
Normal file
|
@ -0,0 +1,7 @@
|
|||
[settings]
|
||||
debug = false
|
||||
api_key = ba50f683-bb6b-4f31-9c84-7c70412234f7
|
||||
[projectmap]
|
||||
samples/projects/project_map:_with_colon/ = proj-map-nomatch-1
|
||||
samples/projects/project_map = proj-map-match
|
||||
samples/projects/project_map:_with_colon/ = proj-map-nomatch-2
|
|
@ -0,0 +1,9 @@
|
|||
[settings]
|
||||
debug = false
|
||||
api_key = ba50f683-bb6b-4f31-9c84-7c70412234f7
|
||||
[projectmap]
|
||||
samples/projects/project_map/ = proj-map-duplicate-1
|
||||
samples/projects/project_map/ = proj-map-duplicate-2
|
||||
samples/projects/project_map/ = proj-map-duplicate-3
|
||||
samples/projects/project_map/ = proj-map-duplicate-4
|
||||
samples/projects/project_map/ = proj-map-duplicate-5
|
|
@ -345,7 +345,10 @@ class ProjectTestCase(utils.TestCase):
|
|||
self.assertEquals('hg', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
|
||||
self.assertEquals('default', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['branch'])
|
||||
|
||||
def test_project_map(self):
|
||||
@log_capture()
|
||||
def test_project_map(self, logs):
|
||||
logging.disable(logging.NOTSET)
|
||||
|
||||
response = Response()
|
||||
response.status_code = 0
|
||||
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
|
||||
|
@ -360,7 +363,17 @@ class ProjectTestCase(utils.TestCase):
|
|||
|
||||
self.assertEquals('proj-map', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
|
||||
|
||||
def test_project_map_group_usage(self):
|
||||
self.assertEquals(sys.stdout.getvalue(), '')
|
||||
self.assertEquals(sys.stderr.getvalue(), '')
|
||||
|
||||
log_output = "\n".join([u(' ').join(x) for x in logs.actual()])
|
||||
expected = u('')
|
||||
self.assertEquals(log_output, expected)
|
||||
|
||||
@log_capture()
|
||||
def test_project_map_group_usage(self, logs):
|
||||
logging.disable(logging.NOTSET)
|
||||
|
||||
response = Response()
|
||||
response.status_code = 0
|
||||
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
|
||||
|
@ -375,6 +388,13 @@ class ProjectTestCase(utils.TestCase):
|
|||
|
||||
self.assertEquals('proj-map42', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
|
||||
|
||||
self.assertEquals(sys.stdout.getvalue(), '')
|
||||
self.assertEquals(sys.stderr.getvalue(), '')
|
||||
|
||||
log_output = "\n".join([u(' ').join(x) for x in logs.actual()])
|
||||
expected = u('')
|
||||
self.assertEquals(log_output, expected)
|
||||
|
||||
@log_capture()
|
||||
def test_project_map_with_invalid_regex(self, logs):
|
||||
logging.disable(logging.NOTSET)
|
||||
|
@ -423,3 +443,53 @@ class ProjectTestCase(utils.TestCase):
|
|||
log_output = "\n".join([u(' ').join(x) for x in logs.actual()])
|
||||
expected = u('WakaTime WARNING Regex error (tuple index out of range) for projectmap pattern: proj-map{3}')
|
||||
self.assertEquals(log_output, expected)
|
||||
|
||||
@log_capture()
|
||||
def test_project_map_allows_duplicate_keys(self, logs):
|
||||
logging.disable(logging.NOTSET)
|
||||
|
||||
response = Response()
|
||||
response.status_code = 0
|
||||
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
|
||||
|
||||
now = u(int(time.time()))
|
||||
entity = 'tests/samples/projects/project_map/emptyfile.txt'
|
||||
config = 'tests/samples/configs/project_map_with_duplicate_keys.cfg'
|
||||
|
||||
args = ['--file', entity, '--config', config, '--time', now]
|
||||
|
||||
execute(args)
|
||||
|
||||
self.assertEquals('proj-map-duplicate-5', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
|
||||
|
||||
self.assertEquals(sys.stdout.getvalue(), '')
|
||||
self.assertEquals(sys.stderr.getvalue(), '')
|
||||
|
||||
log_output = "\n".join([u(' ').join(x) for x in logs.actual()])
|
||||
expected = u('')
|
||||
self.assertEquals(log_output, expected)
|
||||
|
||||
@log_capture()
|
||||
def test_project_map_allows_colon_in_key(self, logs):
|
||||
logging.disable(logging.NOTSET)
|
||||
|
||||
response = Response()
|
||||
response.status_code = 0
|
||||
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
|
||||
|
||||
now = u(int(time.time()))
|
||||
entity = 'tests/samples/projects/project_map/emptyfile.txt'
|
||||
config = 'tests/samples/configs/project_map_with_colon_in_key.cfg'
|
||||
|
||||
args = ['--file', entity, '--config', config, '--time', now]
|
||||
|
||||
execute(args)
|
||||
|
||||
self.assertEquals('proj-map-match', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
|
||||
|
||||
self.assertEquals(sys.stdout.getvalue(), '')
|
||||
self.assertEquals(sys.stderr.getvalue(), '')
|
||||
|
||||
log_output = "\n".join([u(' ').join(x) for x in logs.actual()])
|
||||
expected = u('')
|
||||
self.assertEquals(log_output, expected)
|
||||
|
|
|
@ -21,9 +21,9 @@ from .constants import CONFIG_FILE_PARSE_ERROR
|
|||
|
||||
|
||||
try:
|
||||
import ConfigParser as configparser
|
||||
except ImportError: # pragma: nocover
|
||||
import configparser
|
||||
except ImportError:
|
||||
from .packages import configparser
|
||||
|
||||
|
||||
def parseConfigFile(configFile=None):
|
||||
|
@ -41,7 +41,7 @@ def parseConfigFile(configFile=None):
|
|||
if not configFile:
|
||||
configFile = os.path.join(os.path.expanduser('~'), '.wakatime.cfg')
|
||||
|
||||
configs = configparser.SafeConfigParser()
|
||||
configs = configparser.SafeConfigParser(delimiters=('='), strict=False)
|
||||
try:
|
||||
with open(configFile, 'r', encoding='utf-8') as fh:
|
||||
try:
|
||||
|
|
1390
wakatime/packages/configparser/__init__.py
Normal file
1390
wakatime/packages/configparser/__init__.py
Normal file
File diff suppressed because it is too large
Load diff
171
wakatime/packages/configparser/helpers.py
Normal file
171
wakatime/packages/configparser/helpers.py
Normal file
|
@ -0,0 +1,171 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from collections import MutableMapping
|
||||
try:
|
||||
from collections import UserDict
|
||||
except ImportError:
|
||||
from UserDict import UserDict
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from ordereddict import OrderedDict
|
||||
|
||||
from io import open
|
||||
import sys
|
||||
try:
|
||||
from thread import get_ident
|
||||
except ImportError:
|
||||
try:
|
||||
from _thread import get_ident
|
||||
except ImportError:
|
||||
from _dummy_thread import get_ident
|
||||
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
str = type('str')
|
||||
|
||||
|
||||
def from_none(exc):
|
||||
"""raise from_none(ValueError('a')) == raise ValueError('a') from None"""
|
||||
exc.__cause__ = None
|
||||
exc.__suppress_context__ = True
|
||||
return exc
|
||||
|
||||
|
||||
# from reprlib 3.2.1
|
||||
def recursive_repr(fillvalue='...'):
|
||||
'Decorator to make a repr function return fillvalue for a recursive call'
|
||||
|
||||
def decorating_function(user_function):
|
||||
repr_running = set()
|
||||
|
||||
def wrapper(self):
|
||||
key = id(self), get_ident()
|
||||
if key in repr_running:
|
||||
return fillvalue
|
||||
repr_running.add(key)
|
||||
try:
|
||||
result = user_function(self)
|
||||
finally:
|
||||
repr_running.discard(key)
|
||||
return result
|
||||
|
||||
# Can't use functools.wraps() here because of bootstrap issues
|
||||
wrapper.__module__ = getattr(user_function, '__module__')
|
||||
wrapper.__doc__ = getattr(user_function, '__doc__')
|
||||
wrapper.__name__ = getattr(user_function, '__name__')
|
||||
wrapper.__annotations__ = getattr(user_function, '__annotations__', {})
|
||||
return wrapper
|
||||
|
||||
return decorating_function
|
||||
|
||||
# from collections 3.2.1
|
||||
class _ChainMap(MutableMapping):
|
||||
''' A ChainMap groups multiple dicts (or other mappings) together
|
||||
to create a single, updateable view.
|
||||
|
||||
The underlying mappings are stored in a list. That list is public and can
|
||||
accessed or updated using the *maps* attribute. There is no other state.
|
||||
|
||||
Lookups search the underlying mappings successively until a key is found.
|
||||
In contrast, writes, updates, and deletions only operate on the first
|
||||
mapping.
|
||||
|
||||
'''
|
||||
|
||||
def __init__(self, *maps):
|
||||
'''Initialize a ChainMap by setting *maps* to the given mappings.
|
||||
If no mappings are provided, a single empty dictionary is used.
|
||||
|
||||
'''
|
||||
self.maps = list(maps) or [{}] # always at least one map
|
||||
|
||||
def __missing__(self, key):
|
||||
raise KeyError(key)
|
||||
|
||||
def __getitem__(self, key):
|
||||
for mapping in self.maps:
|
||||
try:
|
||||
return mapping[key] # can't use 'key in mapping' with defaultdict
|
||||
except KeyError:
|
||||
pass
|
||||
return self.__missing__(key) # support subclasses that define __missing__
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self[key] if key in self else default
|
||||
|
||||
def __len__(self):
|
||||
return len(set().union(*self.maps)) # reuses stored hash values if possible
|
||||
|
||||
def __iter__(self):
|
||||
return iter(set().union(*self.maps))
|
||||
|
||||
def __contains__(self, key):
|
||||
return any(key in m for m in self.maps)
|
||||
|
||||
@recursive_repr()
|
||||
def __repr__(self):
|
||||
return '{0.__class__.__name__}({1})'.format(
|
||||
self, ', '.join(map(repr, self.maps)))
|
||||
|
||||
@classmethod
|
||||
def fromkeys(cls, iterable, *args):
|
||||
'Create a ChainMap with a single dict created from the iterable.'
|
||||
return cls(dict.fromkeys(iterable, *args))
|
||||
|
||||
def copy(self):
|
||||
'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]'
|
||||
return self.__class__(self.maps[0].copy(), *self.maps[1:])
|
||||
|
||||
__copy__ = copy
|
||||
|
||||
def new_child(self): # like Django's Context.push()
|
||||
'New ChainMap with a new dict followed by all previous maps.'
|
||||
return self.__class__({}, *self.maps)
|
||||
|
||||
@property
|
||||
def parents(self): # like Django's Context.pop()
|
||||
'New ChainMap from maps[1:].'
|
||||
return self.__class__(*self.maps[1:])
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.maps[0][key] = value
|
||||
|
||||
def __delitem__(self, key):
|
||||
try:
|
||||
del self.maps[0][key]
|
||||
except KeyError:
|
||||
raise KeyError('Key not found in the first mapping: {!r}'.format(key))
|
||||
|
||||
def popitem(self):
|
||||
'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.'
|
||||
try:
|
||||
return self.maps[0].popitem()
|
||||
except KeyError:
|
||||
raise KeyError('No keys found in the first mapping.')
|
||||
|
||||
def pop(self, key, *args):
|
||||
'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].'
|
||||
try:
|
||||
return self.maps[0].pop(key, *args)
|
||||
except KeyError:
|
||||
raise KeyError('Key not found in the first mapping: {!r}'.format(key))
|
||||
|
||||
def clear(self):
|
||||
'Clear maps[0], leaving maps[1:] intact.'
|
||||
self.maps[0].clear()
|
||||
|
||||
|
||||
try:
|
||||
from collections import ChainMap
|
||||
except ImportError:
|
||||
ChainMap = _ChainMap
|
Loading…
Reference in a new issue