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

is subsequence #5

Merged
merged 1 commit into from
Oct 8, 2018
Merged
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
5 changes: 1 addition & 4 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
[bdist_wheel]
universal = 1

[metadata]
license_file = LICENSE
universal = true

[coverage:run]
branch = True
Expand Down
18 changes: 9 additions & 9 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from setuptools import find_packages, setup
from setuptools import setup

setup(
name='wimpy',
version='0.4',
description='Anti-copy-pasta',
url='https://github.com/wimglenn/wimpy',
author='Wim Glenn',
author_email='hey@wimglenn.com',
license='MIT',
packages=find_packages(exclude=['tests']),
name="wimpy",
version="0.5",
description="Anti-copy-pasta",
url="https://github.com/wimglenn/wimpy",
author="Wim Glenn",
author_email="hey@wimglenn.com",
license="MIT",
packages=["wimpy"],
)
97 changes: 63 additions & 34 deletions tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,35 @@

import pytest

from wimpy import WimpyError
from wimpy import cached_property
from wimpy import ceiling_division
from wimpy import chunks
from wimpy import grouper
from wimpy import is_subsequence
from wimpy import strip_prefix
from wimpy import strip_suffix
from wimpy import WimpyError
from wimpy import working_directory


@pytest.mark.parametrize('in_,pre,out', [
('www.yahoo.com', 'www.', 'yahoo.com'),
('www.yahoo.com', 'Www.', 'www.yahoo.com'),
])
@pytest.mark.parametrize(
"in_,pre,out",
[
("www.yahoo.com", "www.", "yahoo.com"),
("www.yahoo.com", "Www.", "www.yahoo.com"),
],
)
def test_strip_prefix(in_, pre, out):
assert strip_prefix(in_, pre) == out


@pytest.mark.parametrize('in_,suf,out', [
('www.yahoo.com', 'www.', 'www.yahoo.com'),
('www.yahoo.com', '.com', 'www.yahoo'),
])
@pytest.mark.parametrize(
"in_,suf,out",
[
("www.yahoo.com", "www.", "www.yahoo.com"),
("www.yahoo.com", ".com", "www.yahoo"),
],
)
def test_strip_suffix(in_, suf, out):
assert strip_suffix(in_, suf) == out

Expand All @@ -45,11 +52,11 @@ def prop(self):

def test_cached_property():
a = A()
assert 'prop' not in a.__dict__
assert "prop" not in a.__dict__
x = a.prop
assert 'prop' in a.__dict__
assert "prop" in a.__dict__
y = a.prop
assert 0. < x == y < 1.
assert 0.0 < x == y < 1.0
del a.prop # invalidate cache
z = a.prop # prop recomputed
assert x == y != z
Expand All @@ -60,45 +67,48 @@ def test_cached_property():
assert isinstance(A.prop, cached_property)


@pytest.mark.parametrize('numerator,denominator,result', [
(51, 10, 6),
(50, 10, 5),
(49, 10, 5),
(-49, 10, -4),
])
@pytest.mark.parametrize(
"numerator,denominator,result",
[(51, 10, 6), (50, 10, 5), (49, 10, 5), (-49, 10, -4)],
)
def test_ceiling_div(numerator, denominator, result):
assert ceiling_division(numerator, denominator) == result



@pytest.mark.parametrize('iterable,n,fillvalue,result', [
(range(6), 2, None, [(0, 1), (2, 3), (4, 5)]),
(range(5), 2, None, [(0, 1), (2, 3), (4, None)]),
(range(5), 2, 123, [(0, 1), (2, 3), (4, 123)]),
])
@pytest.mark.parametrize(
"iterable,n,fillvalue,result",
[
(range(6), 2, None, [(0, 1), (2, 3), (4, 5)]),
(range(5), 2, None, [(0, 1), (2, 3), (4, None)]),
(range(5), 2, 123, [(0, 1), (2, 3), (4, 123)]),
],
)
def test_grouper(iterable, n, fillvalue, result):
assert list(grouper(iterable, n, fillvalue)) == result


@pytest.mark.parametrize('iterable,chunk_size,overlap,result', [
('1234567', 3, 0, [('1', '2', '3'), ('4', '5', '6'), ('7',)]),
('123456', 3, 0, [('1', '2', '3'), ('4', '5', '6')]),
('123456', 4, 2, [('1', '2', '3', '4'), ('3', '4', '5', '6')]),
('', 3, 0, []),
])
@pytest.mark.parametrize(
"iterable,chunk_size,overlap,result",
[
("1234567", 3, 0, [("1", "2", "3"), ("4", "5", "6"), ("7",)]),
("123456", 3, 0, [("1", "2", "3"), ("4", "5", "6")]),
("123456", 4, 2, [("1", "2", "3", "4"), ("3", "4", "5", "6")]),
("", 3, 0, []),
],
)
def test_chunks(iterable, chunk_size, overlap, result):
assert list(chunks(iterable, chunk_size, overlap)) == result


def test_chunks_doesnt_get_stuck_due_to_small_chunk_size():
gen = chunks('123456', chunk_size=0)
with pytest.raises(WimpyError, match='chunk size too small'):
gen = chunks("123456", chunk_size=0)
with pytest.raises(WimpyError, match="chunk size too small"):
next(gen)


def test_chunks_doesnt_get_stuck_due_to_big_overlap():
gen = chunks('123456', chunk_size=3, overlap=3)
with pytest.raises(WimpyError, match='overlap too large'):
gen = chunks("123456", chunk_size=3, overlap=3)
with pytest.raises(WimpyError, match="overlap too large"):
next(gen)


Expand All @@ -108,3 +118,22 @@ def test_chunks_from_infinite_generator():
assert next(g) == (0, 0, 0, 0, 0)
assert next(g) == (0, 0, 0, 0, 0)
assert next(g) == (0, 0, 0, 0, 0)


@pytest.mark.parametrize(
"needle,haystack,result",
[
("23", "1234", True),
("32", "1234", False),
("24", "1234", True),
("1234", "1234", True),
("1234", "123", False),
("113", "112233", True),
("1113", "112233", False),
("", "x", True),
("", "", True),
("x", "", False),
],
)
def test_is_subsequence(needle, haystack, result):
assert is_subsequence(needle, haystack) == result
2 changes: 1 addition & 1 deletion wimpy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from wimpy.exceptions import WimpyError
from wimpy.util import *

__version__ = '0.4'
__version__ = "0.5"
30 changes: 25 additions & 5 deletions wimpy/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,24 @@
from wimpy.exceptions import WimpyError


__all__ = ['cached_property', 'working_directory', 'strip_prefix', 'strip_suffix', 'ceiling_division', 'grouper', 'chunks']
__all__ = [
"cached_property",
"working_directory",
"strip_prefix",
"strip_suffix",
"ceiling_division",
"grouper",
"chunks",
"is_subsequence",
]


class cached_property(object):
"""
non-data descriptor: property is computed once per instance and then replaces itself
with an ordinary attribute. Deleting the attribute invalidates the cache.
"""

def __init__(self, func):
update_wrapper(self, func)
self.func = func
Expand All @@ -41,22 +51,22 @@ def working_directory(path):
def strip_prefix(s, prefix):
"""Removes the prefix, if it's there, otherwise returns input string unchanged"""
if s.startswith(prefix):
return s[len(prefix):]
return s[len(prefix) :]
return s


def strip_suffix(s, suffix):
"""Removes the suffix, if it's there, otherwise returns input string unchanged"""
if s.endswith(suffix):
return s[:len(s) - len(suffix)]
return s[: len(s) - len(suffix)]
return s


def ceiling_division(numerator, denominator):
"""Divide and round up"""
# Implementation relies on Python's // operator using floor division (round down)
# This might surprise C users who expect rounding towards zero
return -(-numerator//denominator)
return -(-numerator // denominator)


def grouper(iterable, n, fillvalue=None):
Expand Down Expand Up @@ -87,4 +97,14 @@ def chunks(iterable, chunk_size=3, overlap=0):
except StopIteration:
# if the iterator is exhausted, yield any remaining elements
if i > 0:
yield tuple(queue)[-i-overlap:]
yield tuple(queue)[-i - overlap :]


def is_subsequence(needle, haystack):
"""Are all the elements of needle contained in haystack, and in the same order?
There may be other elements interspersed throughout"""
it = iter(haystack)
for element in needle:
if element not in it:
return False
return True