Repository: codespell-project/codespell

Issue:
--skip function not always working correctly
codespell 1.12.0.dev0

Running `codespell -q 3 --skip="./src/translator_*"` on https://github.com/doxygen/doxygen does not filter out the ./src/translator_(country-code) files. See snippet:  
```
./src/translator_fr.h:1089: rouge  ==> rogue
./src/translator_fr.h:1098: rouge  ==> rogue
./src/translator_fr.h:1495: correspondant  ==> correspondent
./src/translator_fr.h:1499: correspondant  ==> correspondent
./src/translator_fr.h:1831: correspondance  ==> correspondence
./src/translator_ro.h:158: definit  ==> definite
./src/translator_ro.h:224: Exemple  ==> Example
./src/translator_ro.h:233: complet  ==> complete
./src/translator_ro.h:914: Atribute  ==> Attribute
./src/translator_ro.h:920: Atribute  ==> Attribute
./src/translator_ro.h:928: Atribute  ==> Attribute
./src/translator_ro.h:932: Atribute  ==> Attribute
./src/translator_ro.h:940: Atribute  ==> Attribute
./src/translator_ro.h:944: Atribute  ==> Attribute
./src/translator_ro.h:980: includ  ==> include
./src/translator_ro.h:1205: Clas  ==> Class  | disabled because of name clash in c++
./src/translator_ro.h:1205: clas  ==> class  | disabled because of name clash in c++
./src/translator_ro.h:1284: Autor  ==> Author
./src/translator_ro.h:1284: autor  ==> author
./src/translator_ro.h:1390: Atribute  ==> Attribute
./src/translator_ro.h:1397: Atribute  ==> Attribute
./src/translator_ro.h:1498: complet  ==> complete
./src/translator_sv.h:29: hel  ==> help, hell, heal
./src/translator_sv.h:41: som  ==> some
./src/translator_sv.h:48: som  ==> some
./src/translator_sv.h:59: som  ==> some
./src/translator_sv.h:77: Attribut  ==> Attribute
./src/translator_sv.h:102: Propertie  ==> Property, properties
./src/translator_sv.h:116: Variabel  ==> Variable
./src/translator_sv.h:978: attribut  ==> attribute
./src/translator_sv.h:983: attribut  ==> attribute
./src/translator_sv.h:991: attribut  ==> attribute
./src/translator_sv.h:995: attribut  ==> attribute
./src/translator_sv.h:1003: attribut  ==> attribute
./src/translator_sv.h:1007: attribut  ==> attribute
./src/translator_sv.h:1043: som  ==> some
./src/translator_sv.h:1066: som  ==> some
./src/translator_sv.h:1076: som  ==> some
./src/translator_sv.h:1080: som  ==> some
./src/translator_sv.h:1082: som  ==> some
./src/translator_sv.h:1084: som  ==> some
./src/translator_sv.h:1086: som  ==> some
./src/translator_sv.h:1104: som  ==> some
./src/translator_sv.h:1121: som  ==> some
./src/translator_sv.h:1123: som  ==> some
./src/translator_sv.h:1498: som  ==> some
./src/translator_sv.h:1502: som  ==> some
./src/translator_sv.h:1506: som  ==> some
./src/translator_sv.h:1585: som  ==> some
./src/translator_sv.h:1878: Ons  ==> Owns
./src/htmlentity.cpp:25: enitity  ==> entity
./src/doxygen.cpp:8406: igore  ==> ignore
./src/doxygen.cpp:10052: initialisation  ==> initialization
./src/pyscanner.l:64: statics  ==> statistics
./src/memberdef.cpp:1910: statics  ==> statistics
./src/tclscanner.l:429: scaned  ==> scanned
./src/tclscanner.l:471: initialised  ==> initialized
./src/translator_id.h:92: Variabel  ==> Variable
./src/translator_id.h:176: Variabel  ==> Variable
./src/translator_id.h:244: variabel  ==> variable
./src/translator_id.h:259: variabel  ==> variable
./src/translator_id.h:287: variabel  ==> variable
./src/translator_id.h:424: Variabel  ==> Variable
./src/translator_id.h:460: Variabel  ==> Variable
./src/translator_id.h:878: Variabel  ==> Variable
./src/translator_id.h:1530: Variabel  ==> Variable
./src/translator_tr.h:1037: Mor  ==> More
./src/translator_pt.h:1269: Autor  ==> Author
./src/translator_pt.h:1269: autor  ==> author
./src/translator_pt.h:1833: autor  ==> author
./src/util.cpp:3994: statics  ==> statistics
./src/util.cpp:4754: contructor  ==> constructor
./src/translator_lv.h:1279: Autor  ==> Author
./src/translator_lv.h:1279: autor  ==> author
./src/translator_es.h:24: posible  ==> possible
./src/translator_es.h:1312: Autor  ==> Author
./src/translator_es.h:1312: autor  ==> author
./src/classdef.cpp:161: abstact  ==> abstract
./src/commentcnv.l:670: commments  ==> comments
./src/translator_cz.h:194: Hierarchie  ==> Hierarchy
./src/translator_cz.h:384: hierarchie  ==> hierarchy
./src/translator_cz.h:916: hierarchie  ==> hierarchy
./src/translator_cz.h:920: hierarchie  ==> hierarchy
./src/translator_cz.h:924: hierarchie  ==> hierarchy
./src/translator_cz.h:1501: Hierarchie  ==> Hierarchy
./src/translator_cz.h:1520: hierarchie  ==> hierarchy
./src/configgen.py:66: explicitely  ==> explicitly
./src/translator_sr.h:1257: Autor  ==> Author
./src/translator_sr.h:1257: autor  ==> author
./src/translator_lt.h:785: faile  ==> failed
./src/translator_de.h:99: Propertys  ==> Properties
./src/translator_de.h:214: Elemente  ==> Element, elements
./src/translator_de.h:222: Elemente  ==> Element, elements
./src/translator_de.h:226: Elemente  ==> Element, elements
./src/translator_de.h:297: Elemente  ==> Element, elements
./src/translator_de.h:379: Elemente  ==> Element, elements
./src/translator_de.h:798: Elemente  ==> Element, elements
./src/translator_de.h:1207: Propertys  ==> Properties
./src/translator_de.h:1213: Propertys  ==> Properties
./src/translator_de.h:1393: Autor  ==> Author
./src/translator_de.h:1450: Elemente  ==> Element, elements
./src/translator_de.h:1640: Methode  ==> Method
./src/translator_de.h:2005: Elemente  ==> Element, elements
./src/translator_ca.h:82: Funcions  ==> Functions
./src/translator_ca.h:86: funcions  ==> functions
./src/translator_ca.h:86: funcions  ==> functions
./src/translator_ca.h:102: Funcions  ==> Functions
./src/translator_ca.h:157: definit  ==> definite
./src/translator_ca.h:303: funcions  ==> functions
./src/translator_ca.h:313: als  ==> also
./src/translator_ca.h:313: als  ==> also
./src/translator_ca.h:436: Funcions  ==> Functions
./src/translator_ca.h:472: Funcions  ==> Functions
./src/translator_ca.h:579: funcions  ==> functions
./src/translator_ca.h:992: exemple  ==> example
./src/translator_ca.h:1030: als  ==> also
./src/translator_ca.h:1270: Autor  ==> Author
./src/translator_ca.h:1270: autor  ==> author
./src/translator_ca.h:1362: Funcions  ==> Functions
./src/translator_ca.h:1369: Funcions  ==> Functions
./src/translator_ca.h:1539: Funcions  ==> Functions
./src/translator_ca.h:1589: Funcions  ==> Functions
./src/translator_ca.h:1595: Funcions  ==> Functions
./src/declinfo.l:39: statics  ==> statistics
./src/translator_si.h:66: atribute  ==> attribute
./src/translator_si.h:1206: expresion  ==> expression
./src/template.cpp:5116: Cound  ==> Could
./src/translator_no.h:23: som  ==> some
./src/translator_no.h:29: som  ==> some
./src/translator_no.h:30: som  ==> some
./src/translator_no.h:326: som  ==> some
./src/translator_no.h:998: som  ==> some
./src/translator_no.h:1026: som  ==> some
./src/translator_no.h:1036: som  ==> some
./src/translator_no.h:1038: som  ==> some
./src/translator_no.h:1481: som  ==> some
./src/translator_no.h:1485: som  ==> some
./src/condparser.cpp:54: delimeter  ==> delimiter
./src/condparser.cpp:87: delimeter  ==> delimiter
./src/condparser.cpp:158: delimeters  ==> delimiters
./src/translator_cn.h:103: documention  ==> documentation
```


Discussion:
@peternewman care to weigh in?
Hi @luzpaz ,

The relevant code is here:
https://github.com/codespell-project/codespell/blob/f9f8d1fcca90c521baee52f397e2b3fc7f37f94c/codespell_lib/_codespell.py#L630-L639

Looking at that, I think if you change your skip to `--skip="translator_*"` it will work. But I think we should fix the underlying bug, which is that the glob_match.match is done on file_ which is just the filename part, rather than fname which is the full path (from os.path.join).

I'd suggest looking at the test first:
https://github.com/codespell-project/codespell/blob/50bcf82833aaae8f99fc9b82d886c7191c117e0c/codespell_lib/tests/test_basic.py#L259-L277

Fixing that (so the tests break), then fixing the code to fix the now broken tests.

Although the tests seem to pass matching just the directory, but actually I think that's because https://github.com/codespell-project/codespell/blob/f9f8d1fcca90c521baee52f397e2b3fc7f37f94c/codespell_lib/_codespell.py#L630 matches just the directory.

So adding a line like this:
`assert cs.main('--skip=*ignoredir/bad*', d) == 1`

Should be correct (in terms of testing your intended behaviour), as it ignores that file in a folder, but will fail because the code only matches on dirname or filename currently, not path.
Did you want to have a go at fixing that given the info I've given @luzpaz , or are you still expecting someone else to fix it?

Resolving patch:
diff --git a/codespell_lib/_codespell.py b/codespell_lib/_codespell.py
--- a/codespell_lib/_codespell.py
+++ b/codespell_lib/_codespell.py
@@ -631,9 +631,11 @@ def main(*args):
                     del dirs[:]
                     continue
                 for file_ in files:
-                    if glob_match.match(file_):
+                    if glob_match.match(file_):  # skip files
                         continue
                     fname = os.path.join(root, file_)
+                    if glob_match.match(fname):  # skip paths
+                        continue
                     if not os.path.isfile(fname) or not os.path.getsize(fname):
                         continue
                     bad_count += parse_file(fname, colors, summary)


Test patch:
diff --git a/codespell_lib/tests/test_basic.py b/codespell_lib/tests/test_basic.py
--- a/codespell_lib/tests/test_basic.py
+++ b/codespell_lib/tests/test_basic.py
@@ -276,6 +276,7 @@ def test_ignore():
         assert cs.main('--skip=bad*', d) == 0
         assert cs.main('--skip=*ignoredir*', d) == 1
         assert cs.main('--skip=ignoredir', d) == 1
+        assert cs.main('--skip=*ignoredir/bad*', d) == 1
 
 
 def test_check_filename():