How to manipulate VCF contacts using VObject?

269 views Asked by At

suppose i have built a VObject called vobj (e.g., built via vobject.readComponents(vcfStr)) and want to add a new key:value pair to it:

  print('k=%s v=%s' % (k,v))
  try:
      stmnt1 = "vobj.add('%s')" % (k)
      print('stmnt1:"%s"' % stmnt1)
      eval(stmnt1)

      print('vobj after add\n'+20*'#'+'\n')
      vobj.prettyPrint()
      stmnt2 = "vobj.%s.value = '%s'" % (k,v)
      print('\n'+20*'#'+('\nstmnt2:"%s"' % stmnt2))
      eval(stmnt2)
  except Exception as e:
      print('wazzup?!',e)

all the extra prints and the try:except are because i can't make it work! here's the output produced:

  k=bday v=1931-02-10
  stmnt1:"vobj.add('bday')"
  vobj after add
  ####################

   VCARD
      VERSION: 3.0
      PRODID: -//Apple Inc.//Mac OS X 10.12.3//EN
      N:  Foo Bar 
      FN: Foo Bar
      EMAIL: [email protected]
      params for  EMAIL:
         TYPE ['INTERNET', 'WORK', 'pref']
      ...
      BDAY: 

  ####################
  stmnt2:"vobj.bday.value = '1931-02-10'"
  wazzup?! invalid syntax (<string>, line 1)

i have three specific questions:

  1. VObject makes use of object.attribute "dot" notation, and the only way i've found to handle arbitrary key names is using eval(). there must be a more pythonic way?

  2. evaluation of the first statement stmnt1 works, and changes vobj as expected, producing an unbound slot for BDAY. but stmnt2 fails with bad syntax and i don't know why.

  3. i have also tried stmnt2 = "vobj.%s.value = ['%s']" % (k,v), making the value a list, because of the two alternatives on the VObject README:

     j.email.value = '[email protected]'
     ...
     j.org.value = ['Open Source Applications Foundation']
    

    does it matter whether a string atom or list is used?

1

There are 1 answers

1
David Arnold On BEST ANSWER

I tried to simplify your code, and ended up with:

import vobject

s = 'BEGIN:VCARD\r\nFN:John Smith\r\nN:Smith;John;;;\r\nEND:VCARD'
vobj = vobject.readOne(s)

vobj.add('bday')
vobj.prettyPrint()

vobj.bday.value = '1931-02-10'
vobj.prettyPrint()

which produced:

> python test.py
VCARD
   FN: John Smith
   N:  John  Smith 
  BDAY: 
VCARD
   FN: John Smith
   N:  John  Smith 
   BDAY: 1931-02-10
>

which seems to be what was expected.

Question 1 There's not generally a need for arbitrary names in vObjects, because the specifications dictate what values are required, and they're all handled by the vobject code.

A common pattern when adding a value is to save a reference to the added attribute, and access it directly, like:

notes = vobj.add("NOTES")
notes.value = "The content of my notes"

or, just do it inline:

vobj.add("NOTES").value = "Notes in one line"

Or you can access it directly:

vobj.notes.value = "New notes content"

Question 2 The problem here is nothing to do with vobject, but rather with Python. In Python, eval only works for expressions. To dynamically compile and execute a statement (like your stmnt2), you need to use exec.

If you change

eval(stmnt2)

to

exec(stmnt2)

your example code sets the BDAY property without an exception.

See What's the difference between eval, exec, and compile? for a full explanation.

Question 3 Yes, it matters. The type of values of the various attributes of a vObject are determined by the specifications. Some have a single value, others are (optionally) a set of values. vobject doesn't enforce this, currently.