← Back to team overview

pytagsfs team mailing list archive

xattr metastore: an initial attempt

 

Hi,
You'll find attached an initial attempt to create an xattr metastore for
pytagsfs.
It's far from perfect but basically working.
I'm really in need of advices about the _right_ way to implement bases
classes from pytagsfs.
I would also appreciate clarification about "cp -r" / "mv" support.

Finally it would be nice if we can go ahead on the following thread:
https://lists.launchpad.net/pytagsfs/msg00061.html


Thank you in advance.


Raph
# coding: utf-8

# Copyright (c) 2011 Raphaël Droz.
# This file is part of the pytagsfs software package.
#
# pytagsfs is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License version 2 as published by the Free
# Software Foundation.
#
# A copy of the license has been included in the COPYING file.

import os.path

# for debugging purpose
import inspect, traceback, sys, re


import xattr

from pytagsfs.metastore import MetaStore

# todo: implement this ?
from pytagsfs.values import Values


# TODO:
# $ mv mnt/tag1/filename.avi mnt/tag2/filename2.avi
# should fail as 'filename.avi' will be kept (tag will change though)


# TODO: the following fails
# $ mkdir mnt/tag2
# $ cp mnt/tag1/file.avi mnt/tag2/
# as is:
# $ cp -a mnt/tag1 mnt/tag2


def __function__ ():
    caller = inspect.stack()[1]
    return caller[3]

# TODO:
# sync or async...
# with update() we have to (re)set all tags
# (and remove obsolete ones) !

# TODO:
# two consecutive --add for the same key failed

# TODO: inherit from Values if possible !

class SimpleXAttrFile(dict):
    # dictionary containing all attributes and their value
    d = {}
    # filename
    f = None

    def __init__(self,filename):
        print "IMPL:", __function__()
        # test the file is regular/exists/...
        if not os.path.exists(filename):
            print "failed to find", filename
            # todo: how to return (raise ?) a failure here ?
            self.d = None
            return None
        
        # todo: test the filesystem supports xattr
        # or, todo: simply test pyxattr supports it

        self.f = filename
        self.d = dict ( xattr.get_all(filename, namespace=xattr.NS_USER) )

        print "__init__ => d = ", self.d
        # needed when one extends the dict class
        self.update(self.d.keys(), self.d.values())

    # needed (print SimpleXAttrFile)
    def __repr__(self):
        return repr(self.d)

    # used by pytags --add
    def get(self, key, failobj=None):
        print "IMPL:", __function__()
        if key not in self.d:
            return failobj
        return self.d[key]
        
    # needed (by, eg, update and others)
    def __setitem__(self, key, value):
        print "IMPL:", __function__()
        self.d[key] = value
        return

    # needed by pytags --remove
    def __delitem__(self, key):
        print "IMPL:", __function__()
        self.d.pop(key)

    # needed when initialization happens in __init__
    def update(self, args, kwargs):
        print "IMPL:", __function__()
        for k, v in dict(zip(args, kwargs)).iteritems():
            self.d[k] = v

    # needed by ... pytags, why ?
    def save(self):
        print "IMPL:", __function__()
        for k, v in self.d.iteritems():
            # todo:
            # with pytags --set, v = [ value ] so we need v[0]
            # with pytags --add, v = value so we need v
            print "save", k, "=", v
            if isinstance(v, (list, tuple)):
                xattr.set(self.f, k, v[0], namespace=xattr.NS_USER)
            else:
                xattr.set(self.f, k, v, namespace=xattr.NS_USER)

    # needed by all *.items(), Values.from_flat_dict(), ... in the _XattrMetaStore class below
    def items(self):
        print "IMPL:", __function__()
        return self.d.items()

    # followings uneeded ?
    def keys(self):
        print "IMPL:", __function__()
        return self.d.keys()

    def values(self):
        print "IMPL:", __function__()
        return self.d.values()

    def __getitem__(self, key):
        print "IMPL:", __function__()
        return self.d[key]

    def iteritems(self):
        print "IMPL:", __function__()
        return self.d.iteritems()

    def __iteritems__(self):
        print "IMPL:", __function__()
        return self.d.__iteritems__()

    def viewitems(self):
        print "IMPL:", __function__()
        return self.d.viewitems()

    def __contains__(self, key):
        print "IMPL:", __function__()
        return key in self.d


class _XattrMetaStore(MetaStore):

    # needed by pytagsfs --format
    # called by sourcetreerep/__init__.py: add_source_file()
    # through the DelegateMultiMetaStore:get() function
    def get(self, path):
        #traceback.print_stack()
        # todo, how is it merged if one do, eg, attr -s extension=X file.avi ?

        d = dict ( xattr.get_all(path, namespace=xattr.NS_USER ) )
        for k in d:
            d[k] = d[k].split(',')
        values = Values(d)
        return values

    # called by a move on the destination filesystem
    def set(self, path, values):
        print "IMPL:", self.__class__.__name__, __function__()
        #print path
        #print values

        for k,v in values.iteritems():
            # concatenate the elements in a string with ',' as separator
            xattr.set(path, k, ','.join(v), namespace=xattr.NS_USER)

        return values.keys()
        

    def save(self):
        print "IMPL:", self.__class__.__name__, __function__()


    # needed by pytags --format
    @classmethod
    def extract(self, tags):
        print "IMPL:", self.__class__.__name__, __function__()
        values = Values()
        print tags, values
        return tags

    # needed: used by pytags --set    
    @classmethod
    def inject(cls, tags, values):
        print "IMPL:", self.__class__.__name__, __function__()
        # values always seems empty, return
        return

class XattrMetaStore(_XattrMetaStore):
    error_class = Exception

    def tags_class(self, filename):
        return SimpleXAttrFile(filename)
        

# PYTHONPATH=modules ./pytags --metastores=pytagsfs.metastore.xattr_.XattrMetaStore --set subject=eco ~/tagsfs/sources/Into_Eternity.avi

# PYTHONPATH=modules ./pytags --metastores=pytagsfs.metastore.xattr_.XattrMetaStore --format='/%{title}.%{extension}' ~/tagsfs/sources/Into_Eternity.avi

# pytagsfs.metastore.path.PathMetaStore is needed to "inherit" %f, %p and %e !
# (in the last position to override potential conflicting attributes from XattrMetaStore)

# PYTHONPATH=modules ./pytagsfs -ds -o metastores='pytagsfs.metastore.xattr_.XattrMetaStore;pytagsfs.metastore.path.PathMetaStore' -o format='/%{subject}/%f' ~/tagsfs/sources ~/tagsfs/mnt


# PYTHONPATH=modules ./pytagsfs -ds -o metastores='pytagsfs.metastore.maildir.MaildirMetaStore;pytagsfs.metastore.path.PathMetaStore' -o format='/%{maildir_tag}/%f' ~/tagsfs/sources/mail/cur/ ~/tagsfs/mnt/
# ===
# PYTHONPATH=modules ./pymailtagsfs -ds -o format='/%{maildir_tag}/%f' ~/tagsfs/sources/mail/cur/ ~/tagsfs/mnt/

Follow ups