anthrotype: thank you Denis! This looks much better than my hotfix :+1: 

can you remind me why we can't read/write plist files as unicode strings and we need to use bytes?
diff --git a/normalization/ufonormalizer.py b/normalization/ufonormalizer.py
index 7d0fa56..c4f15fb 100644
--- a/normalization/ufonormalizer.py
+++ b/normalization/ufonormalizer.py
@@ -1,6 +1,6 @@
 #! /usr/bin/env python
 # -*- coding: utf-8 -*-
-from __future__ import print_function
+from __future__ import print_function, unicode_literals
 
 import time
 import os
@@ -11,6 +11,7 @@ import textwrap
 import datetime
 import glob
 from collections import OrderedDict
+from io import open
 
 """
 - filter out unknown attributes and subelements
@@ -89,22 +90,22 @@ except NameError:
 # plistlib.readPlistFromString instead.
 if hasattr(plistlib, "loads"):
 
-    def _readPlistFromBytes(data):
+    def _loads(data):
         return plistlib.loads(data)
 
-    def _writePlistToBytes(plist):
+    def _dumps(plist):
         return plistlib.dumps(plist)
 elif hasattr(plistlib, "readPlistFromBytes"):
-    def _readPlistFromBytes(data):
+    def _loads(data):
         return plistlib.readPlistFromBytes(tobytes(data))
 
-    def _writePlistToBytes(plist):
+    def _dumps(plist):
         return plistlib.writePlistToBytes(plist)
 else:
-    def _readPlistFromBytes(data):
+    def _loads(data):
         return plistlib.readPlistFromString(data)
 
-    def _writePlistToBytes(plist):
+    def _dumps(plist):
         return plistlib.writePlistToString(plist)
 
 
@@ -1334,9 +1335,8 @@ def subpathReadFile(ufoPath, *subpath):
     Read the contents of a file.
     """
     path = subpathJoin(ufoPath, *subpath)
-    f = open(path, "rb")
-    text = f.read()
-    f.close()
+    with open(path, "r", encoding="utf-8") as f:
+        text = f.read()
     return text
 
 def subpathReadPlist(ufoPath, *subpath):
@@ -1344,12 +1344,14 @@ def subpathReadPlist(ufoPath, *subpath):
     Read the contents of a property list
     and convert it into a Python object.
     """
-    text = subpathReadFile(ufoPath, *subpath)
-    return _readPlistFromBytes(text)
+    path = subpathJoin(ufoPath, *subpath)
+    with open(path, "rb") as f:
+        data = f.read()
+    return _loads(data)
 
 # write
 
-def subpathWriteFile(data, ufoPath, *subpath):
+def subpathWriteFile(text, ufoPath, *subpath):
     """
     Write data to a file.
 
@@ -1360,19 +1362,12 @@ def subpathWriteFile(data, ufoPath, *subpath):
     path = subpathJoin(ufoPath, *subpath)
     if subpathExists(ufoPath, *subpath):
         existing = subpathReadFile(ufoPath, *subpath)
-
-        if type(data) != type(existing):
-            if not isinstance(data, unicode):
-                data = unicode(data, "utf-8")
-            if not isinstance(existing, unicode):
-                existing = unicode(existing, "utf-8")
     else:
         existing = None
 
-    if data != existing:
-        f = open(path, "wb")
-        f.write(tobytes(data))
-        f.close()
+    if text != existing:
+        with open(path, "w", encoding="utf-8") as f:
+            f.write(text)
 
 def subpathWritePlist(data, ufoPath, *subpath):
     """
@@ -1383,8 +1378,16 @@ def subpathWritePlist(data, ufoPath, *subpath):
     file contains data that is different
     from the new data.
     """
-    data = _writePlistToBytes(data)
-    subpathWriteFile(data, ufoPath, *subpath)
+    data = _dumps(data)
+    path = subpathJoin(ufoPath, *subpath)
+    if subpathExists(ufoPath, *subpath):
+        existing = subpathReadPlist(ufoPath, *subpath)
+    else:
+        existing = None
+
+    if data != existing:
+        with open(path, "wb") as f:
+            f.write(data)
 
 # rename
 

String encoding issue
When using the latest version, I am getting this error. It seems to relate to © symbol in font info area.

```
File "/Users/…/bin/FDK/Tools/osx/Python/Current/lib/python2.7/site-packages/ufonormalizer-0.1-py2.7.egg/ufonormalizer.py", line 109, in tobytes
    return s.encode(encoding, errors)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa9' in position 310: ordinal not in range(128)
```
diff --git a/normalization/test_ufonormalizer.py b/normalization/test_ufonormalizer.py
index 42957dc..6631c7d 100644
--- a/normalization/test_ufonormalizer.py
+++ b/normalization/test_ufonormalizer.py
@@ -11,10 +11,13 @@ from io import open
 from xml.etree import cElementTree as ET
 from ufonormalizer import (
     normalizeGLIF, normalizeGlyphsDirectoryNames, normalizeGlyphNames,
-    subpathJoin, subpathReadPlist, subpathWriteFile, subpathWritePlist,
-    UFONormalizerError, XMLWriter, tobytes, userNameToFileName, handleClash1,
-    handleClash2, xmlEscapeText, xmlEscapeAttribute, xmlConvertValue,
-    xmlConvertFloat, xmlConvertInt,
+    subpathJoin, subpathSplit, subpathExists, subpathReadFile,
+    subpathReadPlist, subpathWriteFile, subpathWritePlist, subpathRenameFile,
+    subpathRenameDirectory, subpathRenameDirectory, subpathRemoveFile,
+    subpathGetModTime, subpathNeedsRefresh, modTimeLibKey, storeModTimes,
+    readModTimes, UFONormalizerError, XMLWriter,
+    tobytes, userNameToFileName, handleClash1, handleClash2, xmlEscapeText,
+    xmlEscapeAttribute, xmlConvertValue, xmlConvertFloat, xmlConvertInt,
     _normalizeGlifAnchor, _normalizeGlifGuideline, _normalizeGlifLib,
     _normalizeGlifNote, _normalizeFontInfoGuidelines, _normalizeGlifUnicode,
     _normalizeGlifAdvance, _normalizeGlifImage, _normalizeDictGuideline,
@@ -25,16 +28,19 @@ from ufonormalizer import (
     _normalizeGlifPointAttributesFormat2,
     _normalizeGlifComponentAttributesFormat2, _normalizeGlifTransformation,
     _normalizeColorString, _convertPlistElementToObject)
+from ufonormalizer import __version__ as ufonormalizerVersion
 
 # Python 3.4 deprecated readPlistFromBytes and writePlistToBytes
 # Python 2 has readPlistFromString and writePlistToString
 try:
-    from plistlib import loads
+    from plistlib import loads, dumps
 except ImportError:
     try:
         from plistlib import readPlistFromBytes as loads
+        from plistlib import writePlistToBytes as dumps
     except ImportError:
         from plistlib import readPlistFromString as loads
+        from plistlib import writePlistToString as dumps
 
 GLIFFORMAT1 = '''\
 <?xml version="1.0" encoding="UTF-8"?>
@@ -1565,6 +1571,142 @@ class XMLWriterTest(unittest.TestCase):
         self.assertEqual(xmlConvertInt(0o0000030), '24')
         self.assertEqual(xmlConvertInt(65536), '65536')
 
+    def test_duplicateUFO(self):
+        pass
+
+
+class SubpathTest(unittest.TestCase):
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        self.filename = 'tmp'
+        self.plistname = 'tmp.plist'
+
+    def setUp(self):
+        self.directory = tempfile.mkdtemp()
+        self.filepath = os.path.join(self.directory, self.filename)
+        self.plistpath = os.path.join(self.directory, self.plistname)
+
+    def tearDown(self):
+        shutil.rmtree(self.directory)
+
+    def createTestFile(self, text, num=None):
+        if num is None:
+            with open(self.filepath, 'w', encoding='utf-8') as f:
+                f.write(text)
+        else:
+            for i in range(num):
+                filepath = self.filepath + str(i)
+                with open(filepath, 'w', encoding='utf-8') as f:
+                    f.write(text)
+
+    def test_subpathJoin(self):
+        self.assertEqual(subpathJoin('a', 'b', 'c'),
+                         os.path.join('a', 'b', 'c'))
+        self.assertEqual(subpathJoin('a', os.path.join('b', 'c')),
+                         os.path.join('a', 'b', 'c'))
+
+    def test_subpathSplit(self):
+        self.assertEqual(subpathSplit(os.path.join('a', 'b')),
+                         os.path.split(os.path.join('a', 'b')))
+        self.assertEqual(subpathSplit(os.path.join('a', 'b', 'c')),
+                         os.path.split(os.path.join('a', 'b', 'c')))
+
+    def test_subpathExists(self):
+        self.createTestFile('')
+        self.assertTrue(subpathExists(self.directory, self.filepath))
+        self.assertFalse(subpathExists(self.directory, 'nofile.txt'))
+
+    def test_subpathReadFile(self):
+        text = 'foo bar™⁜'
+        self.createTestFile(text)
+        self.assertEqual(text, subpathReadFile(self.directory, self.filename))
+
+    def test_subpathReadPlist(self):
+        data = dict([('a', 'foo'), ('b', 'bar'), ('c', '™')])
+        with open(self.plistpath, 'wb') as f:
+            f.write(dumps(data))
+        self.assertEqual(subpathReadPlist(self.directory, self.plistname),
+                         data)
+
+    def test_subpathWriteFile(self):
+        expected_text = 'foo bar™⁜'
+        subpathWriteFile(expected_text, self.directory, self.filename)
+        with open(self.filepath, 'r', encoding='utf-8') as f:
+            text = f.read()
+        self.assertEqual(text, expected_text)
+
+    def test_subpathWritePlist(self):
+        expected_data = dict([('a', 'foo'), ('b', 'bar'), ('c', '™')])
+        subpathWritePlist(expected_data, self.directory, self.plistname)
+        with open(self.plistpath, 'rb') as f:
+            data = loads(f.read())
+        self.assertEqual(data, expected_data)
+
+    def test_subpathRenameFile(self):
+        self.createTestFile('')
+        subpathRenameFile(self.directory, self.filename, self.filename + "_")
+        self.assertTrue(os.path.exists(self.filepath + "_"))
+
+    def test_subpathRenameDirectory(self):
+        dirname = 'tmpdir'
+        dirpath = os.path.join(self.directory, dirname)
+        os.mkdir(dirpath)
+        subpathRenameFile(self.directory, dirname, dirname + "_")
+        self.assertTrue(os.path.exists(dirpath + "_"))
+
+    def test_subpathRemoveFile(self):
+        self.createTestFile('')
+        subpathRemoveFile(self.directory, self.filename)
+        self.assertFalse(os.path.exists(self.filepath))
+
+    def test_subpathGetModTime(self):
+        self.createTestFile('')
+        mtime = subpathGetModTime(self.directory, self.filename)
+        self.assertEqual(os.path.getmtime(self.filepath), mtime)
+
+    def test_subpathNeedsRefresh(self):
+        import time
+        self.createTestFile('')
+        modTime = os.path.getmtime(self.filepath)
+        modTimes = {}
+        modTimes[self.filename] = float(modTime)
+        self.assertFalse(subpathNeedsRefresh(modTimes, self.directory,
+                         self.filename))
+        time.sleep(1)  # to get a different modtime
+        with open(self.filepath, 'w', encoding='utf-8') as f:
+            f.write('foo')
+        self.assertTrue(subpathNeedsRefresh(modTimes, self.directory,
+                        self.filename))
+
+    def test_storeModTimes(self):
+        num = 5
+        lib = {}
+        modTimes = {}
+        self.createTestFile('', num)
+        filenames = [self.filename + str(i) for i in range(num)]
+        for filename in filenames:
+            filepath = os.path.join(self.directory, filename)
+            modTime = os.path.getmtime(filepath)
+            modTimes[filename] = float('%.1f' % (modTime))
+        lines = ['version: %s' % (ufonormalizerVersion)]
+        lines += ['%.1f %s' % (modTimes[filename], filename)
+                  for filename in filenames]
+        storeModTimes(lib, modTimes)
+        self.assertEqual('\n'.join(lines), lib[modTimeLibKey])
+
+    def test_readModTimes(self):
+        num = 5
+        lib = {}
+        modTimes = {}
+        lines = ['version: %s' % (ufonormalizerVersion)]
+        filenames = [self.filename + str(i) for i in range(num)]
+        modTime = float(os.path.getmtime(self.directory))
+        for i, filename in enumerate(filenames):
+            modTimes[filename] = float('%.1f' % (modTime + i))
+            lines.append('%.1f %s' % (modTime + i, filename))
+        lib[modTimeLibKey] = '\n'.join(lines)
+        self.assertEqual(readModTimes(lib), modTimes)
+
 
 class NameTranslationTest(unittest.TestCase):
 

pip install -e .
parse_log_pytest
pytest
pytest coverage
3.4
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
certifi==2021.5.30
coverage==6.2
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
-e git+https://github.com/unified-font-object/ufoNormalizer.git@f14d55967d4621114ff598a4407ee839f5387ff4#egg=ufonormalizer
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: ufoNormalizer
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coverage==6.2
prefix: /opt/conda/envs/ufoNormalizer

normalization/test_ufonormalizer.py::SubpathTest::test_subpathReadFile normalization/test_ufonormalizer.py::SubpathTest::test_subpathWriteFile
normalization/test_ufonormalizer.py::UFONormalizerErrorTest::test_str normalization/test_ufonormalizer.py::UFONormalizerTest::test_convert_plist_Element_to_object normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeFontInfoPlist_guidelines normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeFontInfoPlist_guidelines_everything normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeFontInfoPlist_guidelines_invalid_angle normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeFontInfoPlist_guidelines_invalid_x normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeFontInfoPlist_guidelines_invalid_y normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeFontInfoPlist_guidelines_no_angle normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeFontInfoPlist_guidelines_no_color normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeFontInfoPlist_guidelines_no_identifier normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeFontInfoPlist_guidelines_no_name normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeFontInfoPlist_guidelines_no_x normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeFontInfoPlist_guidelines_no_y normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeFontInfoPlist_no_guidelines normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_advance_defaults normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_advance_height normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_advance_invalid_values normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_advance_undefined normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_advance_width normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_anchor_everything normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_anchor_no_color normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_anchor_no_identifier normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_anchor_no_name normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_anchor_no_x normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_anchor_no_y normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_formats_1_and_2 normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_guideline_everything normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_guideline_invalid normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_image_empty normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_image_everything normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_image_no_color normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_image_no_file_name normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_image_no_transformation normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_lib_defined normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_lib_undefined normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_no_formats normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_note_defined normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_note_undefined normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_outline_format1_element_order normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_outline_format1_empty normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_unicode_with_hex normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGLIF_unicode_without_hex normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_component_attributes_format1_defaults normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_component_attributes_format1_everything normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_component_attributes_format1_no_base normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_component_attributes_format1_no_transformation normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_component_attributes_format2_everything normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_component_format1_everything normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_component_format1_no_base normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_component_format1_subelement normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_contour_format1_empty normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_contour_format1_implied_anchor normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_contour_format1_implied_anchor_with_empty_name normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_contour_format1_implied_anchor_without_name normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_contour_format1_normal normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_contour_format1_point_without_attributes normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_contour_format1_unkown_child_element normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_contour_format1_unkown_point_type normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_contour_format2_empty normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_contour_format2_normal normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_contour_format2_point_without_attributes normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_contour_format2_unknown_child_element normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_outline_format2_element_order normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_outline_format2_empty normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_point_attributes_format1_empty_name normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_point_attributes_format1_everything normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_point_attributes_format1_invalid_x normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_point_attributes_format1_invalid_y normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_point_attributes_format1_no_name normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_point_attributes_format1_no_x normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_point_attributes_format1_no_y normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_point_attributes_format1_subelement normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_point_attributes_format1_type_and_smooth normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_point_attributes_format2_everything normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_transformation_default normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_transformation_empty normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_transformation_invalid_value normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_transformation_non_default normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlif_transformation_unknown_attribute normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlyphNames_non_standard normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlyphNames_old_same_as_new normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlyphsDirectoryNames_non_standard normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeGlyphsDirectoryNames_old_same_as_new normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalizeLayerInfoPlist_color normalization/test_ufonormalizer.py::UFONormalizerTest::test_normalize_color_string normalization/test_ufonormalizer.py::XMLWriterTest::test_attributesToString normalization/test_ufonormalizer.py::XMLWriterTest::test_duplicateUFO normalization/test_ufonormalizer.py::XMLWriterTest::test_propertyListObject_array normalization/test_ufonormalizer.py::XMLWriterTest::test_propertyListObject_boolean normalization/test_ufonormalizer.py::XMLWriterTest::test_propertyListObject_data normalization/test_ufonormalizer.py::XMLWriterTest::test_propertyListObject_date normalization/test_ufonormalizer.py::XMLWriterTest::test_propertyListObject_dict normalization/test_ufonormalizer.py::XMLWriterTest::test_propertyListObject_float normalization/test_ufonormalizer.py::XMLWriterTest::test_propertyListObject_integer normalization/test_ufonormalizer.py::XMLWriterTest::test_propertyListObject_none normalization/test_ufonormalizer.py::XMLWriterTest::test_propertyListObject_string normalization/test_ufonormalizer.py::XMLWriterTest::test_propertyListObject_unknown_data_type normalization/test_ufonormalizer.py::XMLWriterTest::test_xmlConvertFloat normalization/test_ufonormalizer.py::XMLWriterTest::test_xmlConvertInt normalization/test_ufonormalizer.py::XMLWriterTest::test_xmlConvertValue normalization/test_ufonormalizer.py::XMLWriterTest::test_xmlEscapeAttribute normalization/test_ufonormalizer.py::XMLWriterTest::test_xmlEscapeText normalization/test_ufonormalizer.py::SubpathTest::test_readModTimes normalization/test_ufonormalizer.py::SubpathTest::test_storeModTimes normalization/test_ufonormalizer.py::SubpathTest::test_subpathExists normalization/test_ufonormalizer.py::SubpathTest::test_subpathGetModTime normalization/test_ufonormalizer.py::SubpathTest::test_subpathJoin normalization/test_ufonormalizer.py::SubpathTest::test_subpathNeedsRefresh normalization/test_ufonormalizer.py::SubpathTest::test_subpathReadPlist normalization/test_ufonormalizer.py::SubpathTest::test_subpathRemoveFile normalization/test_ufonormalizer.py::SubpathTest::test_subpathRenameDirectory normalization/test_ufonormalizer.py::SubpathTest::test_subpathRenameFile normalization/test_ufonormalizer.py::SubpathTest::test_subpathSplit normalization/test_ufonormalizer.py::SubpathTest::test_subpathWritePlist normalization/test_ufonormalizer.py::NameTranslationTest::test_handleClash1 normalization/test_ufonormalizer.py::NameTranslationTest::test_handleClash1_max_file_length normalization/test_ufonormalizer.py::NameTranslationTest::test_handleClash2 normalization/test_ufonormalizer.py::NameTranslationTest::test_userNameToFileName
normalization/ufonormalizer.py
normalization/ufonormalizer.py