Frank Smit avatar Frank Smit committed 9184a8f

Added support for StringIO, BytesIO, byte strings, unicode strings and file objects.

Comments (0)

Files changed (1)

 # }}}
 # function pofile() {{{
 
-
-def pofile(po_object, wrapwidth=78, encoding=None,
+def pofile(po_resource, wrapwidth=78, encoding=None,
            check_for_duplicates=False, klass=None):
     file_class = klass or POFile
-    if encoding is None:
-        encoding = detect_encoding(po_object)
 
-    parser = _POFileParser(po_object, encoding, check_for_duplicates, klass)
+    resource = Resource(po_resource, encoding)
+
+    parser = _POFileParser(resource, check_for_duplicates, file_class)
+    instance = parser.parse()
+    instance.wrapwidth = wrapwidth  # Bullshit
+    return instance
+
+
+def mofile(mo_resource, wrapwidth=78, encoding=None,
+           check_for_duplicates=False, klass=None):
+    file_class = klass or MOFile
+
+    resource = Resource(mo_resource, encoding)
+
+    parser = _MOFileParser(resource, check_for_duplicates, file_class)
+    instance = parser.parse()
+    instance.wrapwidth = wrapwidth  # Bullshit
+    return instance
 
 
 
 # }}}
 # function mofile() {{{
 
-def mofile(mofile, **kwargs):
+def _mofile(mofile, **kwargs):
     """
     Convenience function that parses the mo file ``mofile`` and returns a
     :class:`~polib.MOFile` instance.
         charset = match.group(1).strip()
         if hasattr(charset, 'decode'):
             charset = charset.decode('utf-8')
-
         codecs.lookup(charset)
     except LookupError:
         charset = default_encoding
     return re.sub(r'\\(\\|n|t|r|")', unescape_repl, st)
 
 # }}}
+# function class Resource {{{
+
+class Resource(object):
+    PATTERN = r'"?Content-Type:.+? charset=([\w_\-:\.]+)'
+    re_u = re.compile(u(PATTERN))
+    re_b = re.compile(b(PATTERN))
+    re_charset = None
+
+    def __init__(self, resource, encoding=None):
+        self._close_file = False
+
+        if isinstance(resource, str):
+            self._close_file = True
+            if '\n' in resource:
+                resource = StringIO(resource)
+            elif os.path.exists(resource) and resource.endswith('.po'):
+                resource = open(resource, 'r')
+            elif os.path.exists(resource) and resource.endswith('.mo'):
+                resource = open(resource, 'rb')
+        elif isinstance(resource, bytes):
+            self._close_file = True
+            if b'\n' in resource:
+                resource = BytesIO(resource)
+            elif os.path.exists(resource) and resource.endswith('.po'):
+                resource = open(resource, 'r')
+            elif os.path.exists(resource) and resource.endswith('.mo'):
+                resource = open(resource, 'rb')
+
+        if hasattr(resource, 'mode'):
+            self.re_charset = self.re_b if resource.mode == 'rb' else self.re_u
+        elif isinstance(resource, StringIO):
+            self.re_charset = self.re_u
+        elif isinstance(resource, BytesIO):
+            self.re_charset = self.re_b
+
+        self._resource = resource
+        self.encoding = self.detect_encoding(encoding)
+
+    def __iter__(self):
+        return self._resource
+
+    def detect_encoding(self, encoding=None):
+        charset = encoding
+        if encoding is None:
+            for line in self._resource:
+                match = self.re_charset.search(line)
+                if match:
+                    charset = match.group(1).strip()
+                    break
+            self.reset()  # Set file pointer to the beginning
+
+        try:
+            if hasattr(charset, 'decode'):
+                charset = charset.decode('utf-8')
+            return codecs.lookup(charset).name
+        except LookupError:
+            return default_encoding
+
+    def read(self, n_bytes=None):
+        return self._resource.read(n_bytes)
+
+    def seek(self, position):
+        return self._resource.seek(position)
+
+    def reset(self):
+        self._resource.seek(0)
+
+    def __del__(self):
+        if self._close_file:
+            self._resource.close()
+
+
+# }}}
 # class _BaseFile {{{
 
 class _BaseFile(list):
         list.__init__(self)
         # the opened file handle
         pofile = kwargs.get('pofile', None)
-        if pofile and os.path.exists(pofile):
-            self.fpath = pofile
-        else:
-            self.fpath = kwargs.get('fpath')
+        # if pofile and os.path.exists(pofile):
+        #     self.fpath = pofile
+        # else:
+        #     self.fpath = kwargs.get('fpath')
+        self.fpath = pofile
         # the width at which lines should be wrapped
         self.wrapwidth = kwargs.get('wrapwidth', 78)
         # the file encoding
         ``repr_method``
             string, the method to use for output.
         """
-        if self.fpath is None and fpath is None:
-            raise IOError('You must provide a file path to save() method')
-        contents = getattr(self, repr_method)()
-        if fpath is None:
-            fpath = self.fpath
-        if repr_method == 'to_binary':
-            fhandle = open(fpath, 'wb')
-        else:
-            fhandle = codecs.open(fpath, 'w', self.encoding)
-            if not isinstance(contents, text_type):
-                contents = contents.decode(self.encoding)
-        fhandle.write(contents)
-        fhandle.close()
-        # set the file path if not set
-        if self.fpath is None and fpath:
-            self.fpath = fpath
+        pass
+        # if self.fpath is None and fpath is None:
+        #     raise IOError('You must provide a file path to save() method')
+        # contents = getattr(self, repr_method)()
+        # if fpath is None:
+        #     fpath = self.fpath
+        # if repr_method == 'to_binary':
+        #     fhandle = open(fpath, 'wb')
+        # else:
+        #     fhandle = codecs.open(fpath, 'w', self.encoding)
+        #     if not isinstance(contents, text_type):
+        #         contents = contents.decode(self.encoding)
+        # fhandle.write(contents)
+        # fhandle.close()
+        # # set the file path if not set
+        # if self.fpath is None and fpath:
+        #     self.fpath = fpath
 
     def find(self, st, by='msgid', include_obsolete_entries=False,
              msgctxt=False):
     file format.
     """
 
-    def __init__(self, pofile, *args, **kwargs):
+    def __init__(self, po_file, *args, **kwargs):
         """
         Constructor.
 
             file (optional, default: ``False``).
         """
         enc = kwargs.get('encoding', default_encoding)
-
-        # TODO: Detect file object type (actual file, string, file-like)
-        if os.path.exists(pofile):
-            try:
-                self.fhandle = codecs.open(pofile, 'rU', enc)
-            except LookupError:
-                enc = default_encoding
-                self.fhandle = codecs.open(pofile, 'rU', enc)
-        else:
-            self.fhandle = pofile.splitlines()
+        self.fhandle = po_file
 
         klass = kwargs.get('klass')
         if klass is None:
         }
 
         for line in self.fhandle:
+            if hasattr(line, 'decode'):
+                line = line.decode(self.fhandle.encoding)
+
             i += 1
             line = line.strip()
             if line == '':
                     if key is not None:
                         self.instance.metadata[key] += '\n'+ msg.strip()
         # close opened file
-        if not isinstance(self.fhandle, list):  # must be file
-            self.fhandle.close()
+
         return self.instance
 
     def add(self, symbol, states, next_state):
             whether to check for duplicate entries when adding entries to the
             file (optional, default: ``False``).
         """
-        self.fhandle = open(mofile, 'rb')
+        self.fhandle = mofile
 
         klass = kwargs.get('klass')
         if klass is None:
             else:
                 entry = self._build_entry(msgid=msgid, msgstr=msgstr)
             self.instance.append(entry)
-        # close opened file
-        self.fhandle.close()
+
         return self.instance
 
     def _build_entry(self, msgid, msgstr=None, msgid_plural=None,
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.