Commits

th3l0nius  committed 7dae1a3

made changes as discussed in bitbucket issue #20http://j.mp/1lg3mtO

  • Participants
  • Parent commits 6a514d2
  • Branches bugfix/issue_20_nobytes_keys

Comments (0)

Files changed (3)

 .idea/
 __pycache__
 .tox
+savReaderWriter.egg-info/
+savReaderWriter/SHA1VERSION

File savReaderWriter/header.py

                 values = [values]
 
             # check if missing values strings values are not too long
-            strMissLabels = [len(v) for v in values if 
+            strMissLabels = [len(v) for v in values if
                              isinstance(v, (str, bytes))]
             if strMissLabels and max(strMissLabels) > 9:
                 raise ValueError("Missing value label > 9 bytes")
         CTABLES."""
         func = self.spssio.spssGetVarMeasureLevel
         levels = {0: b"unknown", 1: b"nominal", 2: b"ordinal", 3: b"scale",
-                  3: b"ratio", 4: b"flag", 5: b"typeless"}
+                  4: b"flag", 5: b"typeless"}
         measureLevel = c_int()
         varMeasureLevels = {}
         for varName in self.varNames:
         if not varMeasureLevels:
             return
         func = self.spssio.spssSetVarMeasureLevel
+        # levels = {b"unknown": 0, b"nominal": 1, b"ordinal": 2, b"scale": 3,
+        #           b"ratio": 3, b"flag": 4, b"typeless": 5}
         levels = {b"unknown": 0, b"nominal": 1, b"ordinal": 2, b"scale": 3,
-                  b"ratio": 3, b"flag": 4, b"typeless": 5}
+                  b"ratio": 3, b"flag": 4, b"typeless": 5,
+                   "unknown": 0,  "nominal": 1,  "ordinal": 2,  "scale": 3,
+                   "ratio": 3,  "flag": 4,  "typeless": 5}
         for varName, level in self.encode(varMeasureLevels).items():
             if level.lower() not in levels:
                 msg = "Valid levels are %s"
-                raise ValueError(msg % b", ".join(levels.keys()).decode())
+                valid_ks = [k for k in levels.keys() if type(k) is str]
+                raise ValueError(msg % ", ".join(valid_ks))
             level = levels.get(level.lower())
             retcode = func(c_int(self.fh), c_char_py3k(varName), c_int(level))
             if retcode:
         if not varAlignments:
             return
         func = self.spssio.spssSetVarAlignment
-        alignments = {b"left": 0, b"right": 1, b"center": 2}
+        alignments = {b"left": 0, b"right": 1, b"center": 2,
+                       "left": 0,  "right": 1,  "center": 2}
         for varName, varAlignment in varAlignments.items():
             if varAlignment.lower() not in alignments:
-                ukeys = b", ".join(alignments.keys()).decode()
+                # valid_ks == alignments.keys() in Python 2
+                # "type('string') is str" is True for both 2 and 3
+                valid_ks = [k for k in alignments.keys() if type(k) is str]
+                ukeys = ", ".join(valid_ks)
                 raise ValueError("Valid alignments are %s" % ukeys)
             alignment = alignments.get(varAlignment.lower())
             retcode = func(c_int(self.fh), c_char_py3k(varName), c_int(alignment))
 
     def _setMultRespDefs(self, multRespDefs):
         """Set 'normal' multiple response defintions.
-        This is a helper function for the multRespDefs setter function. 
+        This is a helper function for the multRespDefs setter function.
         It translates the multiple response definition, specified as a
         dictionary, into a string that the IO module can use"""
         mrespDefs = []
         --extended multiple dichotomy sets: {setName: {"setType": "E",
           "label": lbl, "varNames": [<list_of_varNames>], "countedValue":
            countedValue, 'firstVarIsLabel': <bool>}}
-	Note. You can get values of extended multiple dichotomy sets with 
+	Note. You can get values of extended multiple dichotomy sets with
         getMultRespSetsDefEx, but you cannot write extended multiple dichotomy
         sets.
 
         """Get/Set information that is private to the Data Entry for Windows (DEW)
         product. Returns/takes a dictionary of the form:
         dataEntryInfo = {"data": [<list_of_dew_segments>], "GUID": <guid>},
-        where GUID stands for 'globally unique identifier'. 
+        where GUID stands for 'globally unique identifier'.
         Some remarks:
         -A difference in the byte order of the host system and the foreign host
-         will result in an error. Therefore, an optional 'swapBytes' key may 
-         be specified whose value indicates whether the bytes should be swapped 
+         will result in an error. Therefore, an optional 'swapBytes' key may
+         be specified whose value indicates whether the bytes should be swapped
          (True) or not (False). Default is that the byte order of the host system
          is retained.
         -DEW information is not copied when using mode="cp" in the SavWriter
         is_ascii = all(map(lambda x: ord(x) < 128, asciiGUID))
         if not isinstance(asciiGUID, str) and is_ascii:
             raise ValueError("GUID must be a string of ascii characters")
-        
+
         # I am not sure at all about the following
         swapit = info.has_key("swapBytes") and info.get("swapBytes")
         def swap(x):

File savReaderWriter/unit_tests/test_SavWriter_unicode_options.py

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+##############################################################################
+## Test enhancements from bitbucket issue #20
+##############################################################################
+
+import unittest
+import os, sys
+import tempfile
+from savReaderWriter import *
+
+if sys.version_info[0] == 2:
+    UNI_T, CV_METH = unicode, 'decode'
+else:
+    UNI_T, CV_METH = str, 'encode'
+
+class test_SavWriter_unicode_alignments(unittest.TestCase):
+    """ Write a file, unicode  aligments"""
+
+    def setUp(self):
+        self.savFileName =  os.path.join(tempfile.gettempdir(), "test.sav")
+        varNames = ['var1', 'v2', 'v3', 'bdate']
+        varTypes = {'var1': 6, 'v2': 0, 'v3': 0, 'bdate': 10}
+        measureLevels = {'var1': 'nominal', 'v2': b'scale', 'v3': 'scale', 'bdate': b'ordinal'}
+        alignments = {'var1': 'left', 'v2': b'right', 'v3': u'right', 'bdate': 'center'}
+        self.args = (self.savFileName, varNames, varTypes)
+        self.kwargs = dict(alignments=alignments, measureLevels=measureLevels)
+        # bytified (normalized) copies of the options (in varNames order):
+        self.norm_opts = {on: [getattr(op[n], CV_METH)() if type(op[n]) is UNI_T else op[n]
+                               for n in varNames]
+                          for on,op in self.kwargs.items()}
+
+    def test_SavWriter_alignments(self):
+        records_in = [[b'Test1', 1, 1, b'2010-08-11'],
+                      [b'Test2', 2, 1, b'1910-01-12']]
+
+        with SavWriter(*self.args, **self.kwargs) as writer:
+            for record in records_in:
+                writer.writerow(record)
+
+        with SavHeaderReader(self.savFileName) as header:
+            metadata = header.dataDictionary(True)
+
+        alignments_d = metadata.alignments
+        alignments_l = [alignments_d[n] for n in metadata.varNames]
+        self.assertEqual(alignments_l, self.norm_opts['alignments'])
+
+    def test_SavWriter_measureLevels(self):
+        records_in = [[b'Test1', 1, 1, b'2010-08-11'],
+                      [b'Test2', 2, 1, b'1910-01-12']]
+
+        with SavWriter(*self.args, **self.kwargs) as writer:
+            for record in records_in:
+                writer.writerow(record)
+
+        with SavHeaderReader(self.savFileName) as header:
+            metadata = header.dataDictionary(True)
+
+        measureLevels_d = metadata.measureLevels
+        measureLevels_l = [measureLevels_d[n] for n in metadata.varNames]
+        self.assertEqual(measureLevels_l, self.norm_opts['measureLevels'])
+
+
+    def tearDown(self):
+        os.remove(self.savFileName)
+
+if __name__ == "__main__":
+    unittest.main()