~ubuntu-branches/debian/wheezy/idjc/wheezy

« back to all changes in this revision

Viewing changes to python/prelims/__init__.py

  • Committer: Package Import Robot
  • Author(s): Alessio Treglia
  • Date: 2011-12-03 16:33:59 UTC
  • mfrom: (0.2.6)
  • Revision ID: package-import@ubuntu.com-20111203163359-dq5fy9i756jpoy29
Tags: 0.8.6-1
* New upstream release.
* debian/control:
  - Wrap and sort.
  - Build-depend on autopoint.
  - Drop autotools-dev, unnecessary.
* Drop the whole patch set, none of them is still needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Preliminary initialisation stuff."""
 
2
 
 
3
#   Copyright (C) 2011 Stephen Fairchild (s-fairchild@users.sourceforge.net)
 
4
#   This program is free software: you can redistribute it and/or modify
 
5
#   it under the terms of the GNU General Public License as published by
 
6
#   the Free Software Foundation, either version 2 of the License, or
 
7
#   (at your option) any later version.
 
8
#
 
9
#   This program is distributed in the hope that it will be useful,
 
10
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
#   GNU General Public License for more details.
 
13
#
 
14
#   You should have received a copy of the GNU General Public License
 
15
#   along with this program in the file entitled COPYING.
 
16
#   If not, see <http://www.gnu.org/licenses/>.
 
17
 
 
18
 
 
19
__all__ = ["ArgumentParserImplementation", "ProfileManager"]
 
20
 
 
21
 
 
22
import os
 
23
import sys
 
24
import argparse
 
25
import shutil
 
26
import tempfile
 
27
import time
 
28
import math
 
29
import fcntl
 
30
import subprocess
 
31
from functools import partial
 
32
from collections import defaultdict
 
33
 
 
34
import dbus
 
35
import dbus.service
 
36
from dbus.mainloop.glib import DBusGMainLoop
 
37
DBusGMainLoop(set_as_default=True)
 
38
import glib
 
39
 
 
40
from idjc import FGlobs
 
41
from idjc import PGlobs
 
42
from ..utils import Singleton
 
43
from ..utils import PathStr
 
44
 
 
45
 
 
46
import gettext
 
47
t = gettext.translation(FGlobs.package_name, FGlobs.localedir, fallback=True)
 
48
_ = t.gettext
 
49
 
 
50
 
 
51
 
 
52
# The name of the default profile.
 
53
default = "default"
 
54
 
 
55
 
 
56
# Yes there are too many of these.
 
57
config_files = ("config", "controls", "left_session", "main_session",
 
58
   "main_session_files_played", "main_session_tracks", "playerdefaults",
 
59
   "right_session", "s_data", "mic1", "mic2", "mic3", "mic4", "mic5",
 
60
   "mic6", "mic7", "mic8", "mic9", "mic10", "mic11", "mic12")
 
61
 
 
62
 
 
63
class ArgumentParserError(Exception):
 
64
   pass
 
65
 
 
66
 
 
67
 
 
68
class ArgumentParser(argparse.ArgumentParser):
 
69
   def error(self, text):
 
70
      raise ArgumentParserError(text)
 
71
 
 
72
      
 
73
   def exit_with_message(self, text):
 
74
      """This is just error on the superclass."""
 
75
      
 
76
      super(ArgumentParser, self).error(text)
 
77
 
 
78
 
 
79
 
 
80
class ArgumentParserImplementation(object):
 
81
   """To parse the command line arguments, if any."""
 
82
 
 
83
   __metaclass__ = Singleton
 
84
 
 
85
 
 
86
   def __init__(self, args=None, description=None, epilog=None):
 
87
      if args is None:
 
88
         args = sys.argv[1:]
 
89
 
 
90
      self._args = list(args)
 
91
 
 
92
      if description is None:
 
93
         description = PGlobs.app_longform
 
94
 
 
95
      ap = self._ap = ArgumentParser(description=description, epilog=epilog)
 
96
      ap.add_argument("-v", "--version", action='version', version=
 
97
                     FGlobs.package_name + " " + FGlobs.package_version)
 
98
      sp = self._sp = ap.add_subparsers(
 
99
                     # TC: command line switch info from $ idjc --help
 
100
                     help=_("sub-option -h for more info"))
 
101
      # TC: a command line option help string.
 
102
      sp_run = sp.add_parser("run", help=_("the default command"),
 
103
         # TC: do not translate run.
 
104
         description=description + " " + _("-- sub-command: run"), epilog=epilog)
 
105
      # TC: a command line option help string.
 
106
      sp_mp = sp.add_parser("generateprofile", help=_("make a new profile"),
 
107
         # TC: do not translate generateprofile.
 
108
         description=description + " " + _("-- sub-command: generateprofile"), epilog=epilog)
 
109
 
 
110
      sp_run.add_argument("-d", "--dialog", dest="dialog", nargs=1, 
 
111
            choices=("true", "false"), 
 
112
            help=_("""force the appearance or non-appearance of the
 
113
            profile chooser dialog -- when used with the -p option
 
114
            the chosen profile is preselected"""))
 
115
      # TC: command line help placeholder.
 
116
      sp_run.add_argument("-p", "--profile", dest="profile", nargs=1, metavar=_("profile_choice"), 
 
117
            help=_("""the profile to use -- overrides the user interface
 
118
            preferences "show profile dialog" option"""))
 
119
      sp_run.add_argument("-j", "--jackserver", dest="jackserver", nargs=1,
 
120
            # TC: command line help placeholder.
 
121
            metavar=_("server_name"), help=_("the named jack sound-server to connect with"))
 
122
      group = sp_run.add_argument_group(_("user interface settings"))
 
123
      group.add_argument("-c", "--channels", dest="channels", nargs="+", metavar="c",
 
124
            help=_("the audio channels to have open at startup"))
 
125
      group.add_argument("-V", "--voip", dest="voip", nargs=1, choices=
 
126
            ("off", "private", "public"),
 
127
            help=_("the voip mode at startup"))
 
128
      group.add_argument("-P", "--players", dest="players", nargs="+", metavar="p",
 
129
            help="the players to start among values {1,2}")
 
130
      group.add_argument("-s", "--servers", dest="servers", nargs="+", metavar="s",
 
131
            help=_("attempt connection with the specified servers"))
 
132
      group.add_argument("-x", "--crossfader", dest="crossfader", choices=("1", "2"), 
 
133
            help=_("position the crossfader for the specified player"))
 
134
      # TC: command line help placeholder.
 
135
      sp_mp.add_argument("newprofile", metavar=_("profile_name"),
 
136
            help=_("""new profile name -- will form part of the dbus
 
137
            bus/object/interface name and the JACK client ID --
 
138
            restrictions therefore apply"""))
 
139
      # TC: command line help placeholder.
 
140
      sp_mp.add_argument("-t", "--template", dest="template", metavar=_("template_profile"),
 
141
            help=_("an existing profile to use as a template"))
 
142
      # TC: command line help placeholder.
 
143
      sp_mp.add_argument("-i", "--icon", dest="icon", metavar=_("icon_pathname"),
 
144
            help=_("pathname to an icon -- defaults to idjc logo"))
 
145
      # TC: Command line help placeholder for the profile's nickname.
 
146
      # TC: Actual profile names are very restricted in what characters can be used.
 
147
      sp_mp.add_argument("-n", "--nickname", dest="nickname", metavar=_("nickname"),
 
148
            help=_("""the alternate profile name to appear in window title bars"""))
 
149
      sp_mp.add_argument("-d", "--description", dest="description", metavar=_("description_text"),
 
150
            help=_("a description of the profile"))
 
151
 
 
152
 
 
153
   def parse_args(self):
 
154
      try:
 
155
         return self._ap.parse_args(self._args)
 
156
      except ArgumentParserError as e:
 
157
         try:
 
158
            for cmd in self._sp.choices.iterkeys():
 
159
               if cmd in self._args:
 
160
                  raise
 
161
            return self._ap.parse_args(self._args + ["run"])
 
162
         except ArgumentParserError:
 
163
            self._ap.exit_with_message(str(e))
 
164
 
 
165
 
 
166
   def error(self, text):
 
167
      self._ap.exit_with_message(text)
 
168
      
 
169
      
 
170
   def exit(self, status=0, message=None):
 
171
      self._ap.exit(status, message)
 
172
 
 
173
     
 
174
 
 
175
class DBusUptimeReporter(dbus.service.Object):
 
176
   """Supply uptime to other idjc instances."""
 
177
 
 
178
 
 
179
   interface_name = PGlobs.dbus_bus_basename + ".profile"
 
180
   obj_path  = PGlobs.dbus_objects_basename + "/uptime"
 
181
   
 
182
   
 
183
   def __init__(self):
 
184
      self._uptime_cache = defaultdict(float)
 
185
      self._interface_cache = {}
 
186
      # Defer base class initialisation.
 
187
                           
 
188
                           
 
189
   @dbus.service.method(interface_name, out_signature="d")
 
190
   def get_uptime(self):
 
191
      """Broadcast uptime from the current profile."""
 
192
      
 
193
      return self._get_uptime()
 
194
 
 
195
 
 
196
   def activate_for_profile(self, bus_name, get_uptime):
 
197
      self._get_uptime = get_uptime
 
198
      dbus.service.Object.__init__(self, bus_name, self.obj_path)
 
199
 
 
200
 
 
201
   def get_uptime_for_profile(self, profile):
 
202
      """Ask and return the uptime of an active profile.
 
203
      
 
204
      Step 1, Issue an async request for new data.
 
205
      Step 2, Return immediately with the cached value.
 
206
      
 
207
      Note: On error the cache is purged.
 
208
      """
 
209
 
 
210
 
 
211
      def rh(retval):
 
212
         self._uptime_cache[profile] = retval
 
213
         
 
214
      
 
215
      def eh(exception):
 
216
         try:
 
217
            del self._uptime_cache[profile]
 
218
         except KeyError:
 
219
            pass
 
220
         try:
 
221
            del self._interface_cache[profile]
 
222
         except KeyError:
 
223
            pass
 
224
 
 
225
 
 
226
      try:
 
227
         interface = self._interface_cache[profile]
 
228
      except KeyError:
 
229
         try:
 
230
            p = dbus.SessionBus().get_object(PGlobs.dbus_bus_basename + \
 
231
                                             "." + profile, self.obj_path)
 
232
            interface = dbus.Interface(p, self.interface_name)
 
233
         except dbus.exceptions.DBusException as e:
 
234
            eh(e)
 
235
            return self._uptime_cache.default_factory()
 
236
         
 
237
         self._interface_cache[profile] = interface
 
238
 
 
239
      interface.get_uptime(reply_handler=rh, error_handler=eh)
 
240
      return self._uptime_cache[profile]
 
241
 
 
242
 
 
243
 
 
244
# Profile length limited for practical reasons. For more descriptive
 
245
# purposes the nickname parameter was created.
 
246
MAX_PROFILE_LENGTH = 18
 
247
 
 
248
 
 
249
 
 
250
def profile_name_valid(p):
 
251
   try:
 
252
      dbus.validate_bus_name("com." + p)
 
253
      dbus.validate_object_path("/" + p)
 
254
   except (TypeError, ValueError):
 
255
      return False
 
256
   return len(p) <= MAX_PROFILE_LENGTH
 
257
 
 
258
 
 
259
 
 
260
class ProfileError(Exception):
 
261
   """General purpose exception used within the ProfileManager class.
 
262
   
 
263
   Takes two strings so that one can be used for command line messages
 
264
   and the other for displaying in dialog boxes."""
 
265
   
 
266
   def __init__(self, str1, str2=None):
 
267
      Exception.__init__(self, str1)
 
268
      self.gui_text = str2
 
269
 
 
270
 
 
271
 
 
272
class ProfileManager(object):
 
273
   """The profile gives each application instance a unique identity.
 
274
   
 
275
   This identity extends to the config file directory if present, 
 
276
   to the JACK application ID, to the DBus bus name.
 
277
   """
 
278
   
 
279
   __metaclass__ = Singleton
 
280
   
 
281
 
 
282
   _profile = _dbus_bus_name = _profile_dialog = _init_time = None
 
283
 
 
284
   _optionals = ("icon", "nickname", "description")
 
285
 
 
286
 
 
287
 
 
288
   def __init__(self):
 
289
      ap = ArgumentParserImplementation()
 
290
      args = ap.parse_args()
 
291
 
 
292
      if PGlobs.profile_dir is not None:
 
293
         try:
 
294
            if not os.path.isdir(PGlobs.profile_dir / default):
 
295
               self._generate_default_profile()
 
296
 
 
297
            if "newprofile" in args:
 
298
               self._generate_profile(**vars(args))
 
299
               ap.exit(0)
 
300
         except ProfileError as e:
 
301
            ap.error(_("failed to create profile: %s") % str(e))
 
302
            
 
303
         with open(PGlobs.autoload_profile_pathname, "a"):
 
304
            pass
 
305
 
 
306
         profile = self.autoloadprofilename
 
307
         if profile is None:
 
308
            profile = default
 
309
            dialog_selects = True
 
310
         else:
 
311
            dialog_selects = False
 
312
 
 
313
         if args.profile is not None:
 
314
            profile = args.profile[0]
 
315
            dialog_selects = False
 
316
            if not profile_name_valid(profile):
 
317
               ap.error(_("the specified profile name is not valid"))
 
318
 
 
319
         if args.dialog is not None:
 
320
            dialog_selects = args.dialog[0] == "true"
 
321
         
 
322
         self._uprep = DBusUptimeReporter()
 
323
         self._profile_dialog = self._get_profile_dialog()
 
324
         self._profile_dialog.connect("delete", self._cb_delete_profile)
 
325
         self._profile_dialog.connect("choose", self._choose_profile)
 
326
 
 
327
         def new_profile(dialog, profile, template, icon, nickname, description):
 
328
            try:
 
329
               self._generate_profile(profile, template, icon=icon,
 
330
                           nickname=nickname, description=description)
 
331
               dialog.destroy_new_profile_dialog()
 
332
            except ProfileError as e:
 
333
               dialog.display_error(_("<span weight='bold' size='12000'>Error while creating new profile.</span>\n\n%s") % e.gui_text,
 
334
               transient_parent=dialog.get_new_profile_dialog(), markup=True)
 
335
 
 
336
         self._profile_dialog.connect("new", new_profile)
 
337
         self._profile_dialog.connect("clone", new_profile)
 
338
         self._profile_dialog.connect("edit", self._cb_edit_profile)
 
339
         self._profile_dialog.connect("auto", self._cb_auto)
 
340
         self._profile_dialog.highlight_profile(profile, scroll=True)
 
341
         if dialog_selects:
 
342
            self._profile_dialog.run()
 
343
            self._profile_dialog.hide()
 
344
         else:
 
345
            self._choose_profile(self._profile_dialog, profile, verbose=True)
 
346
         if self._profile is None:
 
347
            ap.error(_("no profile is set"))
 
348
 
 
349
 
 
350
   @property
 
351
   def profile(self):
 
352
      return self._profile
 
353
 
 
354
 
 
355
   @property
 
356
   def iconpathname(self):
 
357
      return self._iconpathname
 
358
 
 
359
 
 
360
   @property
 
361
   def dbus_bus_name(self):
 
362
      return self._dbus_bus_name
 
363
 
 
364
      
 
365
   @property
 
366
   def basedir(self):
 
367
      """The base directory of this profile."""
 
368
      
 
369
      return PGlobs.profile_dir / self.profile
 
370
      
 
371
      
 
372
   @property
 
373
   def jinglesdir(self):
 
374
      """The directory for jingles storage."""
 
375
      
 
376
      return self.basedir / "jingles"
 
377
      
 
378
      
 
379
   @property
 
380
   def title_extra(self):
 
381
      """Window title text indicating which profile is in use."""
 
382
      
 
383
      n = self._nickname
 
384
      if n:
 
385
         return "  (%s:%s)" % ((self.profile, n))
 
386
      else:
 
387
         if self.profile == default:
 
388
            return ""
 
389
         return "  (%s)" % self.profile
 
390
 
 
391
 
 
392
   @property
 
393
   def autoloadprofilename(self):
 
394
      """Which profile would automatically load if given the chance?"""
 
395
      
 
396
      al_profile = self._autoloadprofilename()
 
397
      if al_profile is None:
 
398
         return None
 
399
      
 
400
      try:
 
401
         profiledirs = os.walk(PGlobs.profile_dir).next()[1]
 
402
      except (EnvironmentError, StopIteration):
 
403
         return None
 
404
 
 
405
      return al_profile if al_profile in profiledirs else None
 
406
 
 
407
 
 
408
   @property
 
409
   def profile_dialog(self):
 
410
      return self._profile_dialog
 
411
 
 
412
 
 
413
   def get_uptime(self):
 
414
      if self._init_time is not None:
 
415
         return time.time() - self._init_time
 
416
      else:
 
417
         return 0.0
 
418
 
 
419
 
 
420
   def _autoloadprofilename(self):
 
421
      """Just the file contents without checking."""
 
422
 
 
423
      try:
 
424
         with open(PGlobs.autoload_profile_pathname) as f:
 
425
            fcntl.flock(f, fcntl.LOCK_EX)
 
426
            al_profile = f.readline().strip()
 
427
      except IOError:
 
428
         return None
 
429
      return al_profile
 
430
      
 
431
 
 
432
   def _cb_auto(self, dialog, profile):
 
433
      try:
 
434
         with open(PGlobs.autoload_profile_pathname, "r+") as f:
 
435
            fcntl.flock(f, fcntl.LOCK_EX)
 
436
            al_profile = f.readline().strip()
 
437
            f.seek(0)
 
438
            if profile != al_profile:
 
439
               f.write(profile)
 
440
            f.truncate()
 
441
      except IOError:
 
442
         print "Auto failed!"
 
443
 
 
444
      
 
445
   def _cb_edit_profile(self, dialog, newprofile, oldprofile, *opts):
 
446
      busses = []
 
447
      
 
448
      try:
 
449
         try:
 
450
            busses.append(self._grab_bus_name_for_profile(oldprofile))
 
451
            if newprofile != oldprofile:
 
452
               busses.append(self._grab_bus_name_for_profile(newprofile))
 
453
         except dbus.DBusException:
 
454
            raise ProfileError(None, _("Profile %s is active.") % 
 
455
                                    (oldprofile, newprofile)[len(busses)])
 
456
 
 
457
         if newprofile != oldprofile:
 
458
            try:
 
459
               shutil.copytree(PGlobs.profile_dir / oldprofile,
 
460
                                       PGlobs.profile_dir / newprofile)
 
461
            except EnvironmentError as e:
 
462
               if e.errno == 17:
 
463
                  raise ProfileError(None, 
 
464
                  _("Cannot rename profile {0} to {1}, {1} currently exists.").format(
 
465
                                                oldprofile, newprofile))
 
466
               else:
 
467
                  raise ProfileError(None, 
 
468
                     _("Error during attempt to rename {0} to {1}.").format(
 
469
                                                oldprofile, newprofile))
 
470
 
 
471
            shutil.rmtree(PGlobs.profile_dir / oldprofile)
 
472
 
 
473
         for name, data in zip(self._optionals, opts):
 
474
            with open(PGlobs.profile_dir / newprofile / name, "w") as f:
 
475
               f.write(data or "")
 
476
 
 
477
      except ProfileError, e:
 
478
         text = _("<span weight='bold' size='12000'>Error while editing profile: {0}.</span>\n\n{1}").format(oldprofile, e.gui_text)
 
479
         dialog.display_error(text, markup=True,
 
480
                        transient_parent=dialog.get_new_profile_dialog())
 
481
      else:
 
482
         dialog.destroy_new_profile_dialog()
 
483
      
 
484
      
 
485
   def _cb_delete_profile(self, dialog, profile):
 
486
      if profile is not dialog.profile:
 
487
         try:
 
488
            busname = self._grab_bus_name_for_profile(profile)
 
489
            shutil.rmtree(PGlobs.profile_dir / profile)
 
490
         except dbus.DBusException:
 
491
            pass
 
492
         if profile == default:
 
493
            self._generate_default_profile()
 
494
 
 
495
 
 
496
   def _choose_profile(self, dialog, profile, verbose=False):
 
497
      if dialog.profile is None:
 
498
         try:
 
499
            self._dbus_bus_name = self._grab_bus_name_for_profile(profile)
 
500
         except dbus.DBusException:
 
501
            if verbose:
 
502
               print _("the profile '%s' is in use") % profile
 
503
         else:
 
504
            self._init_time = time.time()
 
505
            self._profile = profile
 
506
            self._nickname = self._grab_profile_filetext(
 
507
                               profile, "nickname") or ""
 
508
            self._iconpathname = self._grab_profile_filetext(
 
509
                               profile, "icon") or PGlobs.default_icon
 
510
            dialog.set_profile(profile, self.title_extra, self._iconpathname)
 
511
            self._uprep.activate_for_profile(self._dbus_bus_name, self.get_uptime)
 
512
      else:
 
513
         print "%s run -p %s" % (FGlobs.bindir / FGlobs.package_name, profile)
 
514
         subprocess.Popen([FGlobs.bindir / FGlobs.package_name, "run", "-p", profile], close_fds=True)
 
515
 
 
516
 
 
517
   def _generate_profile(self, newprofile, template=None, **kwds):
 
518
      if PGlobs.profile_dir is not None:
 
519
         if len(newprofile) > MAX_PROFILE_LENGTH:
 
520
            raise ProfileError(_("the profile length is too long (max %d characters)") % MAX_PROFILE_LENGTH,
 
521
               _("The profile length is too long (max %d characters).") % MAX_PROFILE_LENGTH)
 
522
 
 
523
         if not profile_name_valid(newprofile):
 
524
            raise ProfileError(_("the new profile name is not valid"),
 
525
                                 _("The new profile name is not valid."))
 
526
           
 
527
         try:
 
528
            busname = self._grab_bus_name_for_profile(newprofile)
 
529
         except dbus.DBusException:
 
530
            raise ProfileError(_("the chosen profile is currently running"),
 
531
                                 _("The chosen profile is currently running."))
 
532
 
 
533
         try:
 
534
            tmp = PathStr(tempfile.mkdtemp())
 
535
         except EnvironmentError:
 
536
            raise ProfileError(_("temporary directory creation failed"),
 
537
                                 _("Temporary directory creation failed."))
 
538
            
 
539
         try:
 
540
            if template is not None:
 
541
               if not profile_name_valid(template):
 
542
                  raise ProfileError(
 
543
                        _("the specified template '%s' is not valid") % template,
 
544
                        _("The specified template '%s' is not valid.") % template)
 
545
               
 
546
               tdir = PGlobs.profile_dir / template
 
547
               if os.path.isdir(tdir):
 
548
                  for x in self._optionals + config_files:
 
549
                     try:
 
550
                        shutil.copyfile(tdir / x, tmp / x)
 
551
                     except EnvironmentError:
 
552
                        pass
 
553
                  shutil.copytree(tdir / "jingles", tmp / "jingles")
 
554
               else:
 
555
                  raise ProfileError(
 
556
                     _("the template profile '%s' does not exist") % template,
 
557
                     _("The template profile '%s' does not exist.") % template)
 
558
                  
 
559
            for fname in self._optionals:
 
560
               if kwds.get(fname):
 
561
                  try:
 
562
                     with open(tmp / fname, "w") as f:
 
563
                        f.write(kwds[fname])
 
564
                  except EnvironmentError:
 
565
                     raise ProfileError(_("could not write file %s") + fname,
 
566
                                        _("Could not write file %s.") % fname)
 
567
            
 
568
 
 
569
            dest = PGlobs.profile_dir / newprofile
 
570
            try:
 
571
               shutil.copytree(tmp, dest)
 
572
            except EnvironmentError as e:
 
573
               if e.errno == 17 and os.path.isdir(dest):
 
574
                  msg1 = _("the profile directory '%s' already exists") % dest
 
575
                  msg2 = _("The profile directory '%s' already exists.") % dest
 
576
               else:
 
577
                  msg1 = _("a non directory path exists at: '%s'") % dest
 
578
                  msg2 = _("A Non directory path exists at: '%s'.") % dest
 
579
               raise ProfileError(msg1, msg2)
 
580
         finally:
 
581
            # Failure to clean up is not a critical error.
 
582
            try:
 
583
               shutil.rmtree(tmp)
 
584
            except EnvironmentError:
 
585
               pass
 
586
 
 
587
 
 
588
   def _generate_default_profile(self):
 
589
      self._generate_profile(default, description=_("The default profile"))
 
590
 
 
591
 
 
592
   def _profile_data(self):
 
593
      a = self._autoloadprofilename()
 
594
      d = PGlobs.profile_dir
 
595
      try:
 
596
         profdirs = os.walk(d).next()[1]
 
597
      except (EnvironmentError, StopIteration):
 
598
         return
 
599
      for profname in profdirs:
 
600
         if profile_name_valid(profname):
 
601
            files = os.walk(d / profname).next()[2]
 
602
            rslt = {"profile": profname}
 
603
            for each in self._optionals:
 
604
               try:
 
605
                  with open(d / profname / each) as f:
 
606
                     rslt[each] = f.read()
 
607
               except EnvironmentError:
 
608
                  rslt[each] = None
 
609
               
 
610
            rslt["active"] = self._profile_has_owner(profname)
 
611
            rslt["uptime"] = math.floor(self._uprep.get_uptime_for_profile(profname))
 
612
            rslt["auto"] = (1 if a == profname else 0)
 
613
            yield rslt
 
614
 
 
615
 
 
616
   def closure(cmd, name):
 
617
      busbase = PGlobs.dbus_bus_basename
 
618
      def inner(profname):
 
619
         return cmd(".".join((busbase, profname)))
 
620
      inner.__name__ = name
 
621
      return staticmethod(inner)
 
622
 
 
623
   _profile_has_owner = closure(dbus.SessionBus().name_has_owner,
 
624
                     "_profile_has_owner")
 
625
      
 
626
   _grab_bus_name_for_profile = closure(partial(dbus.service.BusName, do_not_queue=True),
 
627
                     "_grab_bus_name_for_profile")
 
628
                     
 
629
   del closure
 
630
 
 
631
 
 
632
   @staticmethod
 
633
   def _grab_profile_filetext(profile, filename):
 
634
      try:
 
635
         with open(PGlobs.profile_dir / profile / filename) as f:
 
636
            return f.readline().strip()
 
637
      except EnvironmentError:
 
638
         return None
 
639
 
 
640
 
 
641
   def _get_profile_dialog(self):
 
642
      from .profiledialog import ProfileDialog
 
643
      
 
644
      return ProfileDialog(default=default, data_function=self._profile_data)