← Back to team overview

pytagsfs team mailing list archive

xattr metastore: an initial attempt


You'll find attached an initial attempt to create an xattr metastore for
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:

Thank you in advance.

# 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

# $ 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]

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

# 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

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

    # 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)
                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):
        # 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
    def extract(self, tags):
        print "IMPL:", self.__class__.__name__, __function__()
        values = Values()
        print tags, values
        return tags

    # needed: used by pytags --set    
    def inject(cls, tags, values):
        print "IMPL:", self.__class__.__name__, __function__()
        # values always seems empty, 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