[Sugar-devel] [PATCH] Add-clone-to-view-source-submenu

Walter Bender walter.bender at gmail.com
Mon Aug 22 21:01:47 EDT 2011


Thanks for all of this great feedback...

On Mon, Aug 22, 2011 at 5:18 PM, Sascha Silbe <silbe at activitycentral.com> wrote:
> Excerpts from Walter Bender's message of Fri Aug 19 00:11:53 +0200 2011:
>
> [src/jarabe/view/customizebundle.py]
> [...]
>> +import logging
>> +_logger = logging.getLogger('ViewSource')
>
> Please add two empty lines between the imports and the global variable
> to make this easier to read (might also be in PEP-8).
>
>> +CUSTOMICONSUBPATH = 'emblems/emblem-view-source.svg'
>> +BADGETRANSFORM = '  <g transform="matrix(0.45,0,0,0.45,32,32)">\n'
>
> Again for readability, please separate words using underscores ('_').
>
>> +def generate_unique_id():
>> +    ''' Generate an id based on the user's nick name and their public key
>> +    (Based on schema used by IRC activity). '''
>
> For consistency with other parts of the code, please use """ instead of
> '''.
>
>> +    nick = sugar.profile.get_nick_name()
>> +    pubkey = sugar.profile.get_pubkey()
>> +    m = hashlib.sha1()
>> +    m.update(pubkey)
>> +    hexhash = m.hexdigest()
>
> Side note: We should probably add sugar.profile.get_pubkey_hash().
>
>> +    # Get the alphabetic bits of the nick
>> +    nick_letters = "".join([x for x in nick if x.isalpha()])
>
> Again for consistency, please use ' instead of " for strings.
>
>> +    # If that came up with nothing, make it 'XO'.  Also, shorten it if
>> +    # it's more than 11 characters (as we need 5 for - and the hash).
>> +    if len(nick_letters) == 0:
>
> PEP-8: if not nick_letters:
>
>> +        nick_letters = 'XO'
>> +    elif len(nick_letters) > 11:
>> +        nick_letters = nick_letters[0:11]
>
> [:11] would be more intuitive to me, but maybe not to others. We can get
> rid of the conditional, though, since the slice will work even if there
> are fewer letters.
>
>
>> +    # Finally, generate an id by using those letters plus the first
>> +    # four hash bits of the user's public key.
>> +    return(nick_letters + '_' + hexhash[0:4])
>
> Please add a space and remove the parentheses. Return is a statement,
> not a function.
>
>
>> +def generate_bundle(nick, activity_name, home_activities, new_bundle_name):
>> +    ''' Generate a new .xo bundle for the activity and copy it into the
>> +    Journal. '''
>> +
>> +    # First remove any existing bundles in dist/
>> +    if os.path.exists(os.path.join(home_activities, new_bundle_name, 'dist')):
>> +        command_line = ['rm', os.path.join(home_activities, new_bundle_name,
>> +                                           'dist', '*.xo')]
>> +        _logger.debug(subprocess.call(command_line))
>> +        command_line = ['rm', os.path.join(home_activities, new_bundle_name,
>> +                                           'dist', '*.bz2')]
>> +        _logger.debug(subprocess.call(command_line))
>
> Sounds like a job for os.remove() and glob.glob().
>
>> +    # Then create a new bundle.
>> +    config = bundlebuilder.Config(source_dir=os.path.join(
>> +            home_activities, new_bundle_name),
>> +            dist_name='%s_%s-1.xo' % (nick, activity_name))
>> +    bundlebuilder.cmd_fix_manifest(config, None)
>
> We don't have MANIFEST anymore.
>
>> +    bundlebuilder.cmd_dist_xo(config, None)
>
> I'd prefer not to use bundlebuilder in new code as we should replace it
> with distutils [1].

Do we have a good example of using distutils to create a Sugar bundle?

>
>> +    # Finally copy the new bundle to the Journal.
>> +    dsobject = datastore.create()
>> +    dsobject.metadata['title'] = '%s_%s-1.xo' % (nick, activity_name)
>
> I wonder whether we should really use something that looks like a file
> name for the title. I know we do this for files downloaded with Browse,
> but while we could (and should) do better in some cases we don't always
> have a choice. However when cloning an activity, we have all information
> at hand. So how about '%(nick)ss clone of %(activity_name)s'?
>
>
>> +def customize_activity_info(nick, home_activities, new_bundle_name):
>> +    ''' Modify bundle_id in new activity.info file: (1) change the
>> +    bundle_id to bundle_id_[NICKNAME]; (2) change the activity_icon
>> +    [NICKNAME]-activity-icon.svg; (3) set activity_version to 1.
>> +    Also, modify the activity icon by applying a customize overlay.
>> +    '''
>
> The list would be more readable if each item started on a separate line.
>
>> +    activity_name = ''
>> +
>> +    info_old = open(os.path.join(home_activities, new_bundle_name,
>> +                                 'activity', 'activity.info'), 'r')
>> +    info_new = open(os.path.join(home_activities, new_bundle_name,
>> +                                 'activity', 'new_activity.info'), 'w')
>> +    for line in info_old:
>> +        tokens = line.split('=')
>
> This should probably read line.split('=', 1) to prevent us from breaking
> if the value part contains equal signs. We can also give them tokens
> better names:
>
>        name, value = line.split('=', 1)
>
>
>> +        if tokens[0].rstrip() == 'bundle_id':
>
> This isn't performance critical, but reading tokens[0].rstrip() eight
> times in a row hurts my eyes. ;)
>
> How about:
>
>        name, value = [token.strip() for token in line.split('=', 1)]
>
>
>> +            new_bundle_id = '%s_%s_clone' % (tokens[1].strip(), nick)
>> +            info_new.write('%s = %s\n' % (tokens[0].rstrip(), new_bundle_id))
>> +        elif tokens[0].rstrip() == 'activity_version':
>> +            info_new.write('%s = 1\n' % (tokens[0].rstrip()))
>> +        elif tokens[0].rstrip() == 'icon':
>> +            old_icon_name = tokens[1].strip()
>> +            new_icon_name = '%s_%s' % (nick, old_icon_name)
>> +            info_new.write('%s = %s\n' % (tokens[0].rstrip(), new_icon_name))
>> +        elif tokens[0].rstrip() == 'name':
>> +            info_new.write('%s = %s_%s\n' % (tokens[0].rstrip(), nick,
>> +                                           tokens[1].strip()))
>> +            activity_name = tokens[1].strip()
>> +        else:
>> +            info_new.write(line)
>
> With a slightly different structure we could get rid of some duplicated
> formatting:
>
>    for line in info_old:
>        name, value = [token.strip() for token in line.split('=', 1)]
>        if name == 'bundle_id':
>            new_value = '%s_%s_clone' % (value, nick)
>        elif name == 'activity_version':
>            new_value = '1'
>        elif name in ['icon', name]:
>            new_value = '%s_%s' % (nick, value)
>        else:
>            info_new.write(line)
>            continue
>
>        info_new.write('%s = %s\n', name, new_value)
>
>
>> +    info_old.close
>> +    info_new.close
>
> The parentheses are missing, turning these into no-ops (caught by
> pylint).
>
>> +    command_line = ['mv', os.path.join(home_activities, new_bundle_name,
>> +                                       'activity', 'new_activity.info'),
>> +                    os.path.join(home_activities, new_bundle_name,
>> +                                 'activity', 'activity.info')]
>> +    _logger.debug(subprocess.call(command_line))
>
> This can be more easily achieved using os.rename().
>
>> +    _custom_icon(home_activities, new_bundle_name, old_icon_name,
>> +                 new_icon_name)
>
> When reading this, I wondered what that function does and needed to look
> it up. How about _create_custom_icon() instead?
>
>
> [...]
>> +def _custom_icon(home_activities, new_bundle_name, old_icon_name,
>> +                 new_icon_name):
>> +    ''' Modify new activity icon by overlaying CUSTOMICON. '''
>> +
>> +    # First, find CUSTOMICON, which will be used as an overlay.
>> +    icon_path = None
>> +    for path in gtk.icon_theme_get_default().get_search_path():
>> +        if os.path.exists(os.path.join(path, 'sugar', 'scalable',
>> +                                       CUSTOMICONSUBPATH)):
>> +            icon_path = path
>> +            break
>> +
>> +    if icon_path is None:
>> +        # If we cannot find the overlay, just copy the old icon
>> +        _logger.debug('%s not found', CUSTOMICON)
>
> You probably meant CUSTOMICONSUBPATH (caught by pylint).
>
>> +        command_line = ['cp', os.path.join(home_activities, new_bundle_name,
>> +                                           'activity', old_icon_name + '.svg'),
>> +                        os.path.join(home_activities, new_bundle_name,
>> +                                     'activity', new_icon_name + '.svg')]
>> +        _logger.debug(subprocess.call(command_line))
>> +        return
>
> shutil.copy() is a better way to achieve this.
>
>
>> +    # Extract the 'payload' from CUSTOMICON.
>> +    fd_custom = open(os.path.join(icon_path, 'sugar', 'scalable',
>> +                                CUSTOMICONSUBPATH), 'r')
> [...]
>
> I didn't quite grok the code and will leave it for the next iteration of
> review. It does look to be rather complicated however, making me wonder
> whether there's an easier way to achieve at least some of this (e.g.
> parsing the overlay icon).
>
> [...]
>> +    icon_old.close
>> +    icon_new.close
>
> Missing parentheses again.
>
>
> [src/jarabe/view/viewsource.py]
>> +    def __copy_to_home_cb(self, menu_item):
>> +        ''' Make a local copy of the activity bundle in
>> +        $HOME/Activities as [NICK]- '''
>> +
>> +        # TODO: Check to see if a copy of activity already exisits
>> +        # If so, alert the user before overwriting.
>> +        home_activities = os.path.join(os.environ['HOME'], 'Activities')
>
> Please use sugar.env.get_user_activities_path() instead.
>
>> +        nick = generate_unique_id()
>
> This is correct from a purely functional point of view, but could do
> with some better names. At first glance setting the nick name to an
> apparently automatically created unique identifier (which is usually
> random or a number sequence) doesn't look like what we'd want.

I ave struggled with this. I wanted a name that could be used for the
bundle id and incorporated into any posix paths, but to play it safe,
I stuck to alphanumeric characters... probably too strict. I also
wanted to be able to distinguish between clones made by two different
users with the same nick, hence the "unique" factor.

Open to suggestions here.

>
>> +        new_bundle_name = '%s_%s' % (nick, self._document_path.split('/')[-1])
>
> os.path.basename() would be more appropriate here.
>
>> +        command_line = ['cp', '-r', self._document_path,
>> +                        os.path.join(home_activities, new_bundle_name)]
>> +        _logger.debug(subprocess.call(command_line))
>
> shutil.copytree() with symlinks=True is a good candidate for replacing
> the above code.
>
> Sascha
>
> [1] https://bugs.sugarlabs.org/ticket/2947
> --
> http://sascha.silbe.org/
> http://www.infra-silbe.de/
>


-walter
-- 
Walter Bender
Sugar Labs
http://www.sugarlabs.org


More information about the Sugar-devel mailing list