[googlecl] 03/05: Imported Upstream version 0.9.13

Luke Faraone lfaraone at moszumanska.debian.org
Sat Dec 7 22:51:23 UTC 2013


This is an automated email from the git hooks/post-receive script.

lfaraone pushed a commit to branch master
in repository googlecl.

commit 4e4881bb7061f08e9d92a2c4547138fd43cfa08d
Author: Luke Faraone <luke at faraone.cc>
Date:   Sat Dec 7 17:51:08 2013 -0500

    Imported Upstream version 0.9.13
---
 PKG-INFO                          |  2 +-
 README.config                     |  4 +++-
 README.new-usage                  |  3 +++
 changelog                         |  9 ++++++++
 man/google.1                      |  4 ++--
 setup.py                          |  2 +-
 src/google                        | 20 ++++++++++++----
 src/googlecl/__init__.py          | 16 ++++++++++---
 src/googlecl/authentication.py    |  3 ++-
 src/googlecl/base.py              |  2 ++
 src/googlecl/calendar/__init__.py | 24 ++++++++++++++------
 src/googlecl/calendar/service.py  | 48 ++++++++++++++++++++++-----------------
 src/googlecl/config/__init__.py   |  3 +++
 src/googlecl/docs/base.py         | 34 +++++++++++++++++++++++----
 src/googlecl/picasa/service.py    |  2 +-
 15 files changed, 129 insertions(+), 47 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index 7312743..ae11361 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: googlecl
-Version: 0.9.12
+Version: 0.9.13
 Summary: Use (some) Google services from the command line
 Home-page: http://code.google.com/p/googlecl
 Author: Tom H. Miller
diff --git a/README.config b/README.config
index 8fe9faa..7b678b5 100644
--- a/README.config
+++ b/README.config
@@ -32,9 +32,11 @@ Each of these values can be found in any section. The service being run will try
   * editor: [<editor>], The editor to use by default if the document type is not defined by an xxx_editor option. If this is not defined, will use the EDITOR environment variable instead.
   * format: [<extension>], The extension to use by default if the document type is not defined by an xxx_format option.
   * impatient_editors: [<editor1>,<editor2>...], Comma separated list of editors that will not wait for you to finish editing before exiting / returning from the command line. For example, setting this equal to "openoffice.org" (without the quotes) will stop GoogleCL from uploading any changes to Docs until you give it the OK.
+  * invalid_filename_character_sub: [<string>] String to replace invalid filename characters with when editing or downloading documents.  For example, if this is set to !, downloading the file "unfriendly/filename" will rename the file to "unfriendly!filename".  Note that for editing, only the temporary file's name is changed -- it should remain the same online.
 
 1.4 General
-  * auth_browser: [<browser>], Browser to launch when authenticating the OAuth request token. Set this to "disabled" to prevent the launch of any browsers.
+  * auth_browser: [<browser>], Browser to launch when authenticating the OAuth request token. Set this to "disabled" or "none" to prevent the launch of any browsers.
   * date_print_format: [<format string>], Format to use when printing date information. See the Python "time" documentation for formats (http://docs.python.org/library/time.html#time.strftime). For example: "%m %d at %H" for "<month> <day> at <hour>"
+  * default_encoding: [<encoding>], If the terminal encoding is undefined, use this encoding. Odds are, if you are having unicode/ascii decode/encode issues, you'll need to use this setting (almost always 'utf-8' for non-windows users).
   * missing_field_value: [<string>], Placeholder string to use when listing an invalid attribute, for example, the url of a contact.
   * url_style: [site, direct], Which sub-style to use for listing urls. "Site" will typically put you at the website, while "direct" is usually a link directly to the resource.
diff --git a/README.new-usage b/README.new-usage
index 45c4bd7..64c410b 100644
--- a/README.new-usage
+++ b/README.new-usage
@@ -1,5 +1,8 @@
 This document is a quick-guide on getting up to speed with the new version of GoogleCL.
 
+version 0.9.13
+  * Bugfixes only. Booooooring.
+
 version 0.9.12
   * --access will take a variety of text to set access levels on the following items during creation:
       Blogger posts
diff --git a/changelog b/changelog
index a0c4e1c..673920b 100644
--- a/changelog
+++ b/changelog
@@ -1,3 +1,12 @@
+version 0.9.13
+ Bugfixes
+ * default_encoding config option allows user to specify a default encoding other than ASCII (was a bug for some uses, namely cron jobs).
+ * Appropriate intermediate directories are created during authentication token storage.
+ * Catch previously uncaught exception when webbrowser does not find a default or specified browser.
+ * Deletion of recurring calendar events does not cause AttributeError.
+ * Deletion of recurring calendar events with --yes flag only deletes all events if --date is not specified.
+ * File extensions for Picasa are case insensitive.
+
 version 0.9.12
  Enhancements
  * Added support for Google Finance (thanks, bartosh!)
diff --git a/man/google.1 b/man/google.1
index 0e9d034..31ddad5 100644
--- a/man/google.1
+++ b/man/google.1
@@ -1,5 +1,5 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.37.1.
-.TH GOOGLE "1" "January 2011" "google 0.9.12" "User Commands"
+.TH GOOGLE "1" "March 2011" "google 0.9.13" "User Commands"
 .SH NAME
 google \- command-line access to (some) Google services
 .SH SYNOPSIS
@@ -347,7 +347,7 @@ google blogger post \-\-title 'foo' 'command line posting'
 
 google calendar add 'Lunch with Jim at noon tomorrow'
 
-google contacts list name,email >contacts.csv
+google contacts list \-\-title '.*' \-\-fields name,email,phone > contacts.csv
 
 google docs edit \-\-title 'Shopping list'
 
diff --git a/setup.py b/setup.py
index 8639ac7..40dedea 100644
--- a/setup.py
+++ b/setup.py
@@ -66,7 +66,7 @@ command-line tool that makes it easy to do things like posting to a Blogger
 blog, uploading files to Picasa, or editing a Google Docs file."""
 
 setup(name="googlecl",
-      version="0.9.12",
+      version="0.9.13",
       description="Use (some) Google services from the command line",
       author="Tom H. Miller",
       author_email="tom.h.miller at gmail.com",
diff --git a/src/google b/src/google
index 2eeb945..6c973dc 100755
--- a/src/google
+++ b/src/google
@@ -60,7 +60,7 @@ safe_encode = googlecl.safe_encode
 safe_decode = googlecl.safe_decode
 
 
-VERSION = '0.9.12'
+VERSION = '0.9.13'
 
 AVAILABLE_SERVICES = ['picasa', 'blogger', 'youtube', 'docs', 'contacts',
                       'calendar', 'finance']
@@ -109,12 +109,21 @@ def authenticate(auth_manager, options, config, section_header):
       browser_str = config.lazy_get(section_header, 'auth_browser',
                                     default=None)
       if browser_str:
-        if browser_str.lower() == 'disabled':
+        if browser_str.lower() == 'disabled' or browser_str.lower() == 'none':
           browser = None
         else:
-          browser = webbrowser.get(browser_str)
+          try:
+            browser = webbrowser.get(browser_str)
+          except webbrowser.Error, err:
+            LOG.warn(safe_encode(u'Could not get browser "%s": %s' %
+                                 (browser_str, err)))
+            browser = None
       else:
-        browser = webbrowser.get()
+        try:
+          browser = webbrowser.get()
+        except webbrowser.Error, err:
+          LOG.warn(safe_encode(u'Could not get default browser: %s' % err))
+          browser = None
 
       valid_token = auth_manager.retrieve_access_token(display_name, browser)
     if valid_token:
@@ -442,7 +451,8 @@ def run_interactive(parser):
     parser: Object capable of parsing a list of arguments via parse_args.
 
   """
-  history_file = googlecl.get_data_path(googlecl.HISTORY_FILENAME)
+  history_file = googlecl.get_data_path(googlecl.HISTORY_FILENAME,
+                                        create_missing_dir=True)
   try:
     import readline
     try:
diff --git a/src/googlecl/__init__.py b/src/googlecl/__init__.py
index 92e78c5..44ddddf 100644
--- a/src/googlecl/__init__.py
+++ b/src/googlecl/__init__.py
@@ -33,7 +33,7 @@ LOGGER_NAME = __name__
 LOG = logging.getLogger(LOGGER_NAME)
 
 
-def determine_terminal_encoding():
+def determine_terminal_encoding(config=None):
   import sys
   in_enc = ''
   out_enc = ''
@@ -45,9 +45,15 @@ def determine_terminal_encoding():
   # Sometimes these are both defined, and hopefully they are both equal.
   # I'm not sure if they are guaranteed to be equal.
   if in_enc.lower() == out_enc.lower():
-    # If they're not defined, return the python system-wide default encoding
+    # If they're not defined, return the encoding specific in the config file,
+    # or the python system-wide default encoding (if the above is not defined)
+    # Apparently the above values aren't set when run as a cron job or piped?
     if not in_enc:
-      return_enc = sys.getdefaultencoding()
+      return_enc = None
+      if config is not None:
+        return_enc = config.safe_get('GENERAL', 'default_encoding')
+      if return_enc is None:
+        return_enc = sys.getdefaultencoding()
     else:
       return_enc = in_enc
   # If they are not equal, at least one of them must be defined.
@@ -62,6 +68,10 @@ def determine_terminal_encoding():
   return return_enc
 
 
+# Because this gets done at googlecl module load, there's no config parser
+# to pass in.
+#XXX: googlecl.config.load_configuration() currently sets this again,
+# but that's less than optimal...
 TERMINAL_ENCODING = determine_terminal_encoding()
 
 
diff --git a/src/googlecl/authentication.py b/src/googlecl/authentication.py
index 4c752ae..0830037 100644
--- a/src/googlecl/authentication.py
+++ b/src/googlecl/authentication.py
@@ -44,7 +44,8 @@ class AuthenticationManager(object):
       self.tokens_path = tokens_path
     else:
       self.tokens_path = googlecl.get_data_path(TOKENS_FILENAME_FORMAT %
-                                                client.email)
+                                                client.email,
+                                                create_missing_dir=True)
 
   def check_access_token(self):
     """Checks that the client's access token is valid, remove it if not.
diff --git a/src/googlecl/base.py b/src/googlecl/base.py
index b15b30f..b6b9279 100644
--- a/src/googlecl/base.py
+++ b/src/googlecl/base.py
@@ -673,10 +673,12 @@ def compile_entry_string(wrapped_entry, attribute_list, delimiter,
     except ValueError, err:
       LOG.debug(err.args[0] + ' (Did not add value for field ' + attr + ')')
     except AttributeError, err:
+      LOG.debug(err.args[0] + ' (value for field ' + attr + ')')
       try:
         # Last ditch effort to blindly grab the attribute
         val = getattr(wrapped_entry.entry, attr).text or missing_field_value
       except AttributeError:
+        LOG.debug(err.args[0] + ' (value for field ' + attr + ')')
         val = missing_field_value
     # Apparently, atom(?) doesn't always return a Unicode type when there are
     # non-latin characters, so force everything to Unicode.
diff --git a/src/googlecl/calendar/__init__.py b/src/googlecl/calendar/__init__.py
index 8b3c23b..9cef7fb 100644
--- a/src/googlecl/calendar/__init__.py
+++ b/src/googlecl/calendar/__init__.py
@@ -235,12 +235,22 @@ def parse_recurrence(time_string):
 
 
 class CalendarEntryToStringWrapper(googlecl.base.BaseEntryToStringWrapper):
+  def __init__(self, entry, config):
+    """Initialize a CalendarEntry wrapper.
+
+    Args:
+      entry: CalendarEntry to interpret to strings.
+      config: Configuration parser. Needed for some values.
+    """
+    googlecl.base.BaseEntryToStringWrapper.__init__(self, entry)
+    self.config_parser = config
+
   @property
   def when(self):
     """When event takes place."""
     start_date, end_date, freq = get_datetimes(self.entry)
-    print_format = googlecl.CONFIG.lazy_get(SECTION_HEADER,
-                                            'date_print_format')
+    print_format = self.config_parser.lazy_get(SECTION_HEADER,
+                                                      'date_print_format')
     start_text = time.strftime(print_format, start_date)
     end_text = time.strftime(print_format, end_date)
     value = start_text + ' - ' + end_text
@@ -277,9 +287,9 @@ def _list(client, options, args):
 
     for entry in single_events:
       print googlecl.base.compile_entry_string(
-                                            CalendarEntryToStringWrapper(entry),
-                                            options.fields.split(','),
-                                            delimiter=options.delimiter)
+                            CalendarEntryToStringWrapper(entry, client.config),
+                            options.fields.split(','),
+                            delimiter=options.delimiter)
 
 
 #===============================================================================
@@ -356,10 +366,10 @@ def _run_delete(client, options, args):
       if date_range.specified_as_range:
         # if the user specified a date that was a range...
         client.delete_recurring_events(recurring_events, date_range.start,
-                                       date_range.end, cal.user)
+                                       date_range.end, cal.user, options.prompt)
       else:
         client.delete_recurring_events(recurring_events, date_range.start,
-                                       None, cal.user)
+                                       None, cal.user, options.prompt)
     if not (single_events or recurring_events):
       LOG.warning('No events found that match your options!')
 
diff --git a/src/googlecl/calendar/service.py b/src/googlecl/calendar/service.py
index c820284..d890ca8 100644
--- a/src/googlecl/calendar/service.py
+++ b/src/googlecl/calendar/service.py
@@ -130,7 +130,8 @@ class CalendarServiceCL(gdata.calendar.service.CalendarService,
 
   AddReminders = add_reminders
 
-  def delete_recurring_events(self, events, start_date, end_date, cal_user):
+  def delete_recurring_events(self, events, start_date, end_date, cal_user,
+                              prompt):
     """Delete recurring events from a calendar.
 
     Keyword arguments:
@@ -139,6 +140,7 @@ class CalendarServiceCL(gdata.calendar.service.CalendarService,
       end_date: Date specifying the end of events (inclusive). None for no end
           date.
       cal_user: "User" of the calendar to delete events from.
+      prompt: True if we should prompt before deleting events, False otherwise.
 
     """
     # option_list is a list of tuples, (prompt_string, deletion_instruction)
@@ -150,15 +152,18 @@ class CalendarServiceCL(gdata.calendar.service.CalendarService,
     #     'TWIXT' -- delete events between start_date and end_date.
     #     'ON' -- delete events on the single date given.
     #     'ONAFTER' -- delete events on and after the date given.
-    option_list = [('All events in this series', 'ALL')]
+    deletion_choice = 'ALL'
+    option_list = [('All events in this series', deletion_choice)]
     if start_date and end_date:
+      deletion_choice = 'TWIXT'
       option_list.append(('Instances between %s and %s' %
-                          (start_date, end_date), 'TWIXT'))
+                          (start_date, end_date), deletion_choice))
     elif start_date or end_date:
       delete_date = (start_date or end_date)
       option_list.append(('Instances on %s' % delete_date, 'ON'))
       option_list.append(('All events on and after %s' % delete_date,
                           'ONAFTER'))
+      deletion_choice = 'ON'
     option_list.append(('Do not delete', 'NONE'))
     prompt_str = ''
     for i, option in enumerate(option_list):
@@ -167,7 +172,7 @@ class CalendarServiceCL(gdata.calendar.service.CalendarService,
     # multiple times. This is assuming that recurring events have been expanded.
     events = googlecl.calendar.condense_recurring_events(events)
     for event in events:
-      if self.prompt_for_delete:
+      if prompt:
         delete_selection = -1
         while delete_selection < 0 or delete_selection > len(option_list)-1:
           msg = 'Delete "%s"?\n%s' %\
@@ -176,25 +181,26 @@ class CalendarServiceCL(gdata.calendar.service.CalendarService,
             delete_selection = int(raw_input(safe_encode(msg)))
           except ValueError:
             continue
-        option = option_list[delete_selection]
-        if option[1] == 'ALL':
-          self._delete_original_event(event, cal_user)
-        elif option[1] == 'TWIXT':
-          self._batch_delete_recur(event, cal_user,
-                                   start_date=start_date,
-                                   end_date=end_date)
-        elif option[1] == 'ON':
-          self._batch_delete_recur(event, cal_user,
-                                   start_date=delete_date,
-                                   end_date=delete_date)
-        elif option[1] == 'ONAFTER':
-          self._batch_delete_recur(event, cal_user,
-                                   start_date=delete_date)
-        elif option[1] != 'NONE':
-          raise CalendarError('Got unexpected batch deletion command!')
+        deletion_choice = option_list[delete_selection][1]
 
-      else:
+      # deletion_choice has either been picked by the prompt, or is the default
+      # value. The default value is determined by the date info passed in,
+      # and should be the "least destructive" option.
+      if deletion_choice == 'ALL':
         self._delete_original_event(event, cal_user)
+      elif deletion_choice == 'TWIXT':
+        self._batch_delete_recur(event, cal_user,
+                                 start_date=start_date,
+                                 end_date=end_date)
+      elif deletion_choice == 'ON':
+        self._batch_delete_recur(event, cal_user,
+                                 start_date=delete_date,
+                                 end_date=delete_date)
+      elif deletion_choice == 'ONAFTER':
+        self._batch_delete_recur(event, cal_user,
+                                 start_date=delete_date)
+      elif deletion_choice != 'NONE':
+        raise CalendarError('Got unexpected batch deletion command!')
 
   DeleteRecurringEvents = delete_recurring_events
 
diff --git a/src/googlecl/config/__init__.py b/src/googlecl/config/__init__.py
index 48c580e..8e439de 100644
--- a/src/googlecl/config/__init__.py
+++ b/src/googlecl/config/__init__.py
@@ -84,4 +84,7 @@ def load_configuration(path=None):
   made_changes = config.ensure_basic_options(_create_basic_options())
   if made_changes:
     config.write_out_parser()
+  # Set the encoding again, now that the config file is loaded.
+  # (the config file may have a default encoding setting)
+  googlecl.TERMINAL_ENCODING = googlecl.determine_terminal_encoding(config)
   return config
diff --git a/src/googlecl/docs/base.py b/src/googlecl/docs/base.py
index a646d72..26286c4 100644
--- a/src/googlecl/docs/base.py
+++ b/src/googlecl/docs/base.py
@@ -34,6 +34,7 @@ import logging
 import os
 import shlex
 import shutil
+import sys
 import googlecl
 from googlecl.docs import SECTION_HEADER
 
@@ -44,6 +45,12 @@ safe_decode = googlecl.safe_decode
 
 LOG = logging.getLogger(googlecl.docs.LOGGER_NAME + '.base')
 
+# For to_safe_filename
+if sys.platform == 'win32':
+  UNSAFE_FILE_CHARS = '\\/:*?"<>|'
+else:
+  UNSAFE_FILE_CHARS = '/'
+
 
 class DocsError(googlecl.base.Error):
   """Base error for Docs errors."""
@@ -100,9 +107,11 @@ class DocsBaseCL(object):
       base_path = os.path.join(temp_dir, base_folder)
       total_basename = os.path.join(temp_dir, folder_path)
       os.makedirs(total_basename)
-      path = os.path.join(total_basename, doc_title + '.' + file_ext)
+      path = os.path.join(total_basename,
+                          self.to_safe_filename(doc_title) + '.' + file_ext)
     else:
-      path = os.path.join(temp_dir, doc_title + '.' + file_ext)
+      path = os.path.join(temp_dir,
+                          self.to_safe_filename(doc_title) + '.' + file_ext)
       base_path = path
 
     if not new_doc:
@@ -134,7 +143,7 @@ class DocsBaseCL(object):
     if new_doc:
       if isinstance(folder_entry_or_path, basestring):
         # Let code in upload_docs handle the creation of new folder(s)
-        self.upload_docs([base_path])
+        self.upload_docs([base_path], doc_title)
       else:
         # folder_entry_or_path is None or a GDataEntry.
         doc_entry = self.upload_single_doc(path,
@@ -209,7 +218,8 @@ class DocsBaseCL(object):
 
       entry_title = safe_decode(entry.title.text)
       if os.path.isdir(base_path):
-        path = os.path.join(base_path, entry_title + extension)
+        entry_title_safe = self.to_safe_filename(entry_title)
+        path = os.path.join(base_path, entry_title_safe + extension)
       else:
         path = base_path + extension
       LOG.info(safe_encode('Downloading ' + entry_title + ' to ' + path))
@@ -231,6 +241,22 @@ class DocsBaseCL(object):
     """Modify the file data associated with a document entry."""
     raise NotImplementedError('_modify_entry must be defined!')
 
+  def to_safe_filename(self, text):
+    """Translate string to something that can be safely used as a filename.
+
+    Behavior of this function depends on the operating system.
+
+    Args:
+      text: Text to check for invalid characters
+    Returns:
+      Parameter with unsafe characters escaped or removed.
+      Type (unicode vs string)will match that of the parameter.
+    """
+    sub = self.config.lazy_get(SECTION_HEADER, 'invalid_filename_character_sub',
+                               default='')
+    sub = safe_decode(sub)
+    return ''.join([sub if c in UNSAFE_FILE_CHARS else c for c in text])
+
   def upload_docs(self, paths, title=None, folder_entry=None,
                   file_ext=None, **kwargs):
     """Upload a list of documents or directories.
diff --git a/src/googlecl/picasa/service.py b/src/googlecl/picasa/service.py
index c0891d0..0367693 100644
--- a/src/googlecl/picasa/service.py
+++ b/src/googlecl/picasa/service.py
@@ -294,7 +294,7 @@ class PhotosServiceCL(PhotosService, googlecl.service.BaseServiceCL):
         try:
           content_type = SUPPORTED_VIDEO_TYPES[ext]
         except KeyError:
-          content_type = 'image/' + ext
+          content_type = 'image/' + ext.lower()
       if not photo_name:
         photo_name = os.path.split(path)[1]
       try:

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-google/googlecl.git



More information about the Pkg-google-commits mailing list