upload existing project

This commit is contained in:
2026-04-06 17:28:26 +02:00
parent f01c408c87
commit 6bd8fc08bc
12 changed files with 1402 additions and 1 deletions

3
test/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
from .match import MatchTests
from .parse import ParseTests
from .specificity import SpecificityTests

74
test/match.py Normal file
View File

@@ -0,0 +1,74 @@
from unittest import TestCase
import kpmatch
class MatchTests(TestCase):
def test_match_empty(self):
pattern = kpmatch.compile("")
self.assertTrue(pattern.match(""))
self.assertFalse(pattern.match("index.html"))
self.assertFalse(pattern.match("about"))
self.assertFalse(pattern.match("about/about.html"))
self.assertFalse(pattern.match("content/deep/deeper/file.txt"))
def test_match_any(self):
pattern = kpmatch.compile("**")
self.assertTrue(pattern.match(""))
self.assertTrue(pattern.match("index.html"))
self.assertTrue(pattern.match("about"))
self.assertTrue(pattern.match("about/about.html"))
self.assertTrue(pattern.match("content/deep/deeper/file.txt"))
def test_match_any_name(self):
pattern = kpmatch.compile("*.html")
self.assertTrue(pattern.match("index.html"))
self.assertTrue(pattern.match("about.html"))
self.assertFalse(pattern.match("about"))
self.assertFalse(pattern.match("about/about.html"))
self.assertFalse(pattern.match("content/deep/deeper/file.txt"))
def test_match_any_name_anywhere(self):
pattern = kpmatch.compile("**/*.html")
self.assertTrue(pattern.match("index.html"))
self.assertTrue(pattern.match("about.html"))
self.assertFalse(pattern.match("about"))
self.assertTrue(pattern.match("about/about.html"))
self.assertTrue(pattern.match("docs/getting-started/installation.html"))
self.assertFalse(pattern.match("content/deep/deeper/file.txt"))
def test_match_any_directory_name(self):
pattern = kpmatch.compile("packages/*/package.json")
self.assertFalse(pattern.match("packages/package.json"))
self.assertTrue(pattern.match("packages/a/package.json"))
self.assertTrue(pattern.match("packages/b/package.json"))
self.assertFalse(pattern.match("packages/a/b/package.json"))
def test_match_one_of(self):
pattern = kpmatch.compile("*.{png,jpg,jpeg,webp}")
self.assertTrue(pattern.match("image.png"))
self.assertTrue(pattern.match("image.jpg"))
self.assertTrue(pattern.match("image.jpeg"))
self.assertTrue(pattern.match("image.webp"))
self.assertFalse(pattern.match("image.svg"))
def test_match_character_set(self):
pattern = kpmatch.compile("*.[jt]s")
self.assertTrue(pattern.match("file.js"))
self.assertTrue(pattern.match("file.ts"))
self.assertFalse(pattern.match("file.rs"))
self.assertFalse(pattern.match("file.cs"))
def test_match_negative_character_set(self):
pattern = kpmatch.compile("*.[!jt]s")
self.assertFalse(pattern.match("file.js"))
self.assertFalse(pattern.match("file.ts"))
self.assertTrue(pattern.match("file.rs"))
self.assertTrue(pattern.match("file.cs"))
def test_match_any_character(self):
pattern = kpmatch.compile("*.?s")
self.assertTrue(pattern.match("file.js"))
self.assertTrue(pattern.match("file.ts"))
self.assertTrue(pattern.match("file.rs"))
self.assertTrue(pattern.match("file.cs"))

222
test/parse.py Normal file
View File

@@ -0,0 +1,222 @@
import os.path
from unittest import TestCase
from kpmatch import PatternParsingError
import kpmatch.matchers as m
import kpmatch.parse as p
def join_matchers(first: m.PathMatcher, *others: m.PathMatcher) -> m.PathMatcher:
for matcher in others:
first.append(matcher)
return first
class ParseTests(TestCase):
def test_parse_pattern(self):
# pattern <- (any_segments / fixed_segment)*
self.assertEqual(
p._parse_pattern("?a[bc]/**/*.{def,ghi}"),
join_matchers(
m.AnyCharacter(),
m.FixedName("a"),
m.CharacterSet("bc", False),
m.EndOfSegment(),
m.AnySegments(),
m.AnyName(),
m.FixedName("."),
m.OneOf((m.FixedName("def"), m.FixedName("ghi"))),
m.EndOfSegment(),
m.EndOfPath(),
),
)
def test_parse_any_segments(self):
# any_segments <- "**" (SEP / EOS)
self.assertTupleEqual(p._parse_any_segments("**", 0), (m.AnySegments(), 2))
self.assertTupleEqual(p._parse_any_segments("**/a", 0), (m.AnySegments(), 3))
self.assertTupleEqual(p._parse_any_segments("**a", 0), (None, 0))
def test_parse_fixed_segment(self):
# fixed_segment <- name_part+ (SEP / EOS)
self.assertTupleEqual(
p._parse_fixed_segment("?a[bc]/*.{def,ghi}", 0),
(
join_matchers(
m.AnyCharacter(),
m.FixedName("a"),
m.CharacterSet("bc", False),
m.EndOfSegment(),
),
7,
),
)
self.assertTupleEqual(
p._parse_fixed_segment("?a[bc]/*.{def,ghi}", 7),
(
join_matchers(
m.AnyName(),
m.FixedName("."),
m.OneOf((m.FixedName("def"), m.FixedName("ghi"))),
m.EndOfSegment(),
),
18,
),
)
def test_parse_name_part(self):
# name_part <- any_name / one_of / simple_name_part / ","
self.assertTupleEqual(
p._parse_name_part("?a[bc],*{def,ghi}", 0), (m.AnyCharacter(), 1)
)
self.assertTupleEqual(
p._parse_name_part("?a[bc],*{def,ghi}", 1), (m.FixedName("a"), 2)
)
self.assertTupleEqual(
p._parse_name_part("?a[bc],*{def,ghi}", 2), (m.CharacterSet("bc", False), 6)
)
self.assertTupleEqual(
p._parse_name_part("?a[bc],*{def,ghi}", 6), (m.FixedName(","), 7)
)
self.assertTupleEqual(
p._parse_name_part("?a[bc],*{def,ghi}", 7), (m.AnyName(), 8)
)
self.assertTupleEqual(
p._parse_name_part("?a[bc],*{def,ghi}", 8),
(m.OneOf((m.FixedName("def"), m.FixedName("ghi"))), 17),
)
def test_parse_any_name(self):
# any_name = "*"+
self.assertTupleEqual(p._parse_any_name("*a", 0), (m.AnyName(), 1))
self.assertTupleEqual(p._parse_any_name("a*", 1), (m.AnyName(), 2))
self.assertTupleEqual(p._parse_any_name("a***a", 1), (m.AnyName(), 4))
def test_parse_one_of(self):
# one_of = "{" simple_name_part+ ("," simple_name_part+)* "}"
self.assertTupleEqual(p._parse_one_of("", 0), (None, 0))
self.assertTupleEqual(p._parse_one_of("abc,def}", 0), (None, 0))
self.assertTupleEqual(p._parse_one_of("{abc,def", 0), (None, 0))
self.assertTupleEqual(
p._parse_one_of("{abc,def}", 0),
(m.OneOf((m.FixedName("abc"), m.FixedName("def"))), 9),
)
self.assertTupleEqual(
p._parse_one_of("{a[bc],d?f,ghi}", 0),
(
m.OneOf(
(
join_matchers(m.FixedName("a"), m.CharacterSet("bc", False)),
join_matchers(
m.FixedName("d"), m.AnyCharacter(), m.FixedName("f")
),
m.FixedName("ghi"),
)
),
15,
),
)
self.assertRaises(PatternParsingError, p._parse_one_of, "{abc{def}", 0)
self.assertTupleEqual(
p._parse_one_of("{abc{def}", 4), (m.OneOf((m.FixedName("def"),)), 9)
)
self.assertTupleEqual(
p._parse_one_of("{abc}def}", 0), (m.OneOf((m.FixedName("abc"),)), 5)
)
self.assertTupleEqual(
p._parse_one_of(os.path.join("{abc", "def}"), 0),
(None, 0),
)
self.assertTupleEqual(p._parse_one_of("{,def", 0), (None, 0))
self.assertRaises(PatternParsingError, p._parse_one_of, "{,def}", 0)
def test_parse_one_of_choice(self):
# one_of_choice = simple_name_part+
self.assertTupleEqual(
p._parse_one_of_choice("?a[bc],def", 0),
(
join_matchers(
m.AnyCharacter(), m.FixedName("a"), m.CharacterSet("bc", False)
),
6,
),
)
def test_parse_simple_name_part(self):
# simple_name_part = any_character / character_set / fixed_name
self.assertTupleEqual(
p._parse_simple_name_part("?a[bc]", 0), (m.AnyCharacter(), 1)
)
self.assertTupleEqual(
p._parse_simple_name_part("?a[bc]", 1), (m.FixedName("a"), 2)
)
self.assertTupleEqual(
p._parse_simple_name_part("?a[bc]", 2), (m.CharacterSet("bc", False), 6)
)
def test_parse_any_character(self):
self.assertTupleEqual(p._parse_any_character("?a", 0), (m.AnyCharacter(), 1))
self.assertTupleEqual(p._parse_any_character("a?", 1), (m.AnyCharacter(), 2))
def test_parse_character_set(self):
self.assertTupleEqual(p._parse_character_set("", 0), (None, 0))
self.assertTupleEqual(p._parse_character_set("abc]", 0), (None, 0))
self.assertTupleEqual(p._parse_character_set("[abc", 0), (None, 0))
self.assertTupleEqual(
p._parse_character_set("[abcdef]", 0), (m.CharacterSet("abcdef", False), 8)
)
self.assertTupleEqual(
p._parse_character_set("[!abcdef]", 0), (m.CharacterSet("abcdef", True), 9)
)
self.assertTupleEqual(p._parse_character_set("[abcdef]", 3), (None, 3))
self.assertRaises(PatternParsingError, p._parse_character_set, "[abc[def]", 0)
self.assertTupleEqual(
p._parse_character_set("[abc[def]", 4), (m.CharacterSet("def", False), 9)
)
self.assertRaises(PatternParsingError, p._parse_character_set, "[abc!def]", 0)
self.assertTupleEqual(
p._parse_character_set("[abc]def]", 0), (m.CharacterSet("abc", False), 5)
)
self.assertTupleEqual(
p._parse_character_set(os.path.join("[abc", "def]"), 0),
(None, 0),
)
def test_parse_fixed_name(self):
self.assertTupleEqual(p._parse_fixed_name("", 0), (None, 0))
self.assertTupleEqual(
p._parse_fixed_name("abcdef", 0), (m.FixedName("abcdef"), 6)
)
self.assertTupleEqual(
p._parse_fixed_name("abcdef", 2), (m.FixedName("cdef"), 6)
)
self.assertTupleEqual(
p._parse_fixed_name("abc[def", 0), (m.FixedName("abc"), 3)
)
self.assertTupleEqual(
p._parse_fixed_name("abc!def", 0), (m.FixedName("abc"), 3)
)
self.assertTupleEqual(
p._parse_fixed_name("abc]def", 0), (m.FixedName("abc"), 3)
)
self.assertTupleEqual(
p._parse_fixed_name("abc{def", 0), (m.FixedName("abc"), 3)
)
self.assertTupleEqual(
p._parse_fixed_name("abc,def", 0), (m.FixedName("abc"), 3)
)
self.assertTupleEqual(
p._parse_fixed_name("abc}def", 0), (m.FixedName("abc"), 3)
)
self.assertTupleEqual(
p._parse_fixed_name("abc*def", 0), (m.FixedName("abc"), 3)
)
self.assertTupleEqual(
p._parse_fixed_name("abc?def", 0), (m.FixedName("abc"), 3)
)
self.assertTupleEqual(
p._parse_fixed_name(os.path.join("abc", "def"), 0), (m.FixedName("abc"), 3)
)
self.assertTupleEqual(
p._parse_fixed_name(os.path.join("abc", "def"), 2), (m.FixedName("c"), 3)
)

60
test/public_api.py Normal file
View File

@@ -0,0 +1,60 @@
import fnmatch
from configparser import ParsingError
from typing import Callable, Any
from unittest import TestCase
import kpmatch
from kpmatch import PatternParsingError
class ParseTests(TestCase):
def test_compile(self):
pattern = kpmatch.compile("*.txt")
self.assertIsInstance(pattern, kpmatch.Pattern)
self.assertTrue(pattern.match("file.txt"))
self.assertEqual(pattern.specificity, 12)
def test_kpmatch(self):
self.assertTrue(kpmatch.kpmatch("file.txt", "*.txt"))
self.assertFalse(kpmatch.kpmatch("file.jpg", "*.txt"))
def test_is_valid_pattern(self):
self.assertTrue(kpmatch.is_valid_pattern("*.txt"))
self.assertFalse(kpmatch.is_valid_pattern("[.txt"))
def test_specificity(self):
self.assertEqual(kpmatch.specificity(""), 0)
self.assertEqual(kpmatch.specificity("**"), 1)
self.assertEqual(kpmatch.specificity("*.txt"), 12)
self.assertEqual(kpmatch.specificity("file.txt"), 15)
def assertRaisesPPE(self, error_message: str, position: int, pattern: str):
with self.assertRaises(PatternParsingError) as cm:
kpmatch.compile(pattern)
self.assertEqual(
str(PatternParsingError(error_message, pattern, position)),
str(cm.exception),
)
def test_error_messages(self):
self.assertRaisesPPE("Empty path segment", 4, "abc//def")
self.assertRaisesPPE('Missing closing bracket "]" to match "["', 3, "abc[def")
self.assertRaisesPPE(
'Character "!" is not allowed inside a character set', 6, "abc[de!f]"
)
self.assertRaisesPPE(
'Character "[" is not allowed inside a character set', 6, "abc[de[f]"
)
self.assertRaisesPPE('Missing closing bracket "}" to match "{"', 3, "abc{def")
self.assertRaisesPPE(
'Character "{" is not allowed inside a one-of pattern', 6, "abc{de{f}"
)
self.assertRaisesPPE(
'A star "*" wildcard is not allowed inside a one-of pattern',
8,
"abc{def,*}",
)
self.assertRaisesPPE("Empty choice in a one-of pattern", 8, "abc{def,}")
self.assertRaisesPPE("Empty choice in a one-of pattern", 4, "abc{,def}")

21
test/specificity.py Normal file
View File

@@ -0,0 +1,21 @@
from unittest import TestCase
from kpmatch import specificity
class SpecificityTests(TestCase):
def test_compare_specificity(self):
self.assertGreater(specificity("**"), specificity(""))
self.assertGreater(specificity("**/a"), specificity("**"))
self.assertGreater(specificity("**/a/file.txt"), specificity("**/a/*.txt"))
self.assertGreater(specificity("file.txt"), specificity("*.txt"))
self.assertGreater(specificity("*.txt"), specificity("*"))
self.assertGreater(specificity("file.{txt,html}"), specificity("*.txt"))
self.assertGreater(specificity("image_???.png"), specificity("image_*.png"))
self.assertGreater(specificity("image.png"), specificity("image_???.png"))
self.assertEqual(specificity("**/a"), specificity("**/b"))
self.assertEqual(specificity("*.txt"), specificity("*.html"))
self.assertEqual(
specificity("image_?.png"), specificity("image_[0123456789].png")
)