changeset 59:60907204a265 draft

Support case-insensitive filesystems properly.
author David Barts <n5jrn@me.com>
date Fri, 31 May 2019 21:20:22 -0700
parents e08e24707da1
children 682cd33e564c
files install-static tincan.py
diffstat 2 files changed, 20 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/install-static	Fri May 31 20:42:57 2019 -0700
+++ b/install-static	Fri May 31 21:20:22 2019 -0700
@@ -8,6 +8,7 @@
 from argparse import ArgumentParser
 import shutil
 from stat import S_ISDIR, S_ISREG
+from tincan import _casef
 
 # V a r i a b l e s
 
@@ -26,9 +27,9 @@
 
 def copydir(source, target, exclude=True):
     for entry in os.listdir(source):
-        if exclude and entry == WINF:
+        if exclude and _casef(entry, "upper") == WINF:
             continue
-        if entry.startswith(".") or os.path.splitext(entry)[1] in NOT_STATIC:
+        if entry.startswith(".") or _casef(os.path.splitext(entry)[1]) in NOT_STATIC:
             continue
         spath = os.path.join(source, entry)
         tpath = os.path.join(target, entry)
--- a/tincan.py	Fri May 31 20:42:57 2019 -0700
+++ b/tincan.py	Fri May 31 21:20:22 2019 -0700
@@ -252,6 +252,15 @@
         first = False
     return ''.join(ret)
 
+_FOLDS_CASE = sys.platform in ['darwin', 'win32']
+
+def _casef(string, case="lower"):
+    """
+    If we're on an OS with case-insensitive file names, fold case.
+    Else leave things alone.
+    """
+    return getattr(string, case)() if _FOLDS_CASE else string
+
 # The TinCan class. Simply a Bottle webapp that contains a forward method, so
 # the code-behind can call request.app.forward().
 
@@ -353,7 +362,7 @@
             raise ValueError("empty variable name")
         if self.fname == "":
             raise ValueError("empty file name")
-        if not self.fname.endswith(_IEXTEN):
+        if not _casef(self.fname).endswith(_IEXTEN):
             raise ValueError("file does not end in {0}".format(_IEXTEN))
 
 # Using a cache is likely to help efficiency a lot, since many pages
@@ -444,7 +453,7 @@
         self.logger.info("adding static route: %s", self._urlpath)
         self._app.route(self._urlpath, 'GET', self)
         for i in _INDICES:
-            if self._urlpath.endswith(i):
+            if _casef(self._urlpath).endswith(i):
                 li = len(i)
                 for j in [ self._urlpath[:1-li], self._urlpath[:-li] ]:
                     if j:
@@ -582,7 +591,7 @@
         if self._header.python is None:
             self._python_specified = False
         else:
-            if not self._header.python.endswith(_PEXTEN):
+            if not _casef(self._header.python).endswith(_PEXTEN):
                 raise TinCanError("{0}: #python files must end in {1}".format(self._urlpath, _PEXTEN))
             self._python = self._header.python
             self._python_specified = True
@@ -590,7 +599,7 @@
         self._getclass()
         # Build body object (#template) and obtain #loads.
         if self._header.template is not None:
-            if not self._header.template.endswith(_TEXTEN):
+            if not _casef(self._header.template).endswith(_TEXTEN):
                 raise TinCanError("{0}: #template files must end in {1}".format(self._urlpath, _TEXTEN))
             try:
                 rtpath = self._splitpath(self._header.template)
@@ -635,7 +644,7 @@
         mtxt = ','.join(methods)
         self.logger.info("adding route: %s (%s)", self._origin, mtxt)
         self._app.route(self._origin, methods, self)
-        if self._origin.endswith(_INDEX):
+        if _casef(self._origin).endswith(_INDEX):
             for i in [ self._origin[:1-_LINDEX], self._origin[:-_LINDEX] ]:
                 if i:
                     self.logger.info("adding route: %s (%s)", i, mtxt)
@@ -751,7 +760,7 @@
         except IndexError as e:
             raise TinCanError("{0}: invalid #forward".format(self._urlpath)) from e
         name, ext = os.path.splitext(rname)
-        if ext != _TEXTEN:
+        if _casef(ext) != _TEXTEN:
             raise TinCanError("{0}: invalid #forward".format(self._urlpath))
         self._subdir = rlist
         self._python = name + _PEXTEN
@@ -820,7 +829,6 @@
 # L a u n c h e r
 
 _WINF = "WEB-INF"
-_BANNED = set([_WINF])
 _EBANNED = set([_IEXTEN, _TEXTEN, _PEXTEN, _PEXTEN+"c"])
 ENCODING = "utf-8"
 _BITBUCKET = logging.getLogger(__name__)
@@ -886,11 +894,12 @@
         for entry in os.listdir(os.path.join(self.fsroot, *subdir)):
             if entry.startswith("."):
                 continue  # hidden file
-            if not subdir and entry in _BANNED:
+            if not subdir and _casef(entry, "upper") == _WINF:
                 continue
             etype = os.stat(os.path.join(self.fsroot, *subdir, entry)).st_mode
             if S_ISREG(etype):
                 ename, eext = os.path.splitext(entry)
+                eext = _casef(eext)
                 if eext == _TEXTEN:
                     route = _TinCanRoute(self, ename, subdir)
                 else: