Use @property for properties
[joel/kofoto.git] / src / packages / kofoto / clientenvironment.py
1 """Client environment module for Kofoto."""
2
3 __all__ = [
4     "BadConfigFileError",
5     "ClientEnvironment",
6     "ClientEnvironmentError",
7     "ConfigFileError",
8     "MissingConfigFileError",
9     "MissingShelfError",
10 ]
11
12 import codecs
13 import locale
14 import os
15 import sys
16 from kofoto.clientutils import expanduser
17 from kofoto.config import \
18     BadConfigurationValueError, \
19     Config, \
20     DEFAULT_CONFIGFILE_LOCATION, \
21     MissingConfigurationKeyError, \
22     MissingSectionHeaderError, \
23     createConfigTemplate
24 from kofoto.shelf import Shelf, FailedWritingError
25 from kofoto.imagecache import ImageCache
26 from kofoto.version import version as kofotoVersion
27
28 ######################################################################
29 # Public classes.
30
31 class ClientEnvironmentError(Exception):
32     """Base class for exceptions in the module."""
33     pass
34
35 class ConfigFileError(ClientEnvironmentError):
36     """Base class for configuration file exceptions in the module."""
37     pass
38
39 class MissingConfigFileError(ConfigFileError):
40     """Missing configuration file.
41
42     Exception parameter: configuration file location.
43     """
44     pass
45
46 class BadConfigFileError(ConfigFileError):
47     """Bad configuration file.
48
49     Exception parameter: configuration file location.
50     """
51     pass
52
53 class ShelfError(ClientEnvironmentError):
54     """Base class for shelf-related exceptions in the module."""
55     pass
56
57 class MissingShelfError(ShelfError):
58     """Missing shelf.
59
60     Exception parameter: shelf location.
61     """
62     pass
63
64 class BadShelfError(ShelfError):
65     """Bad shelf.
66
67     Exception parameter: shelf location.
68     """
69     pass
70
71 class ClientEnvironment(object):
72     """Environment useful for a Kofoto client.
73
74     A properly initialized ClientEnvironment instance has the
75     following available attributes:
76
77     config             -- A kofoto.config.Config instance.
78     configFileLocation -- Location of the configuration file.
79     filesystemEncoding -- The codeset to use for encoding to and decoding from
80                           paths in the file system.
81     imageCache         -- A kofoto.imagecache.ImageCache instance.
82     localeEncoding     -- The codeset to use for encoding to and decoding from
83                           the current locale.
84     shelf              -- A kofoto.shelf.Shelf instance.
85     shelfLocation      -- Location of the shelf.
86     version            -- Kofoto version (a string).
87     """
88
89     def __init__(self):
90         """Initialize the client environment instance.
91
92         Note that the setup method must be called to further
93         initialize the instance.
94         """
95
96         self.__localeEncoding = locale.getpreferredencoding()
97         self.__filesystemEncoding = sys.getfilesystemencoding()
98
99         # These are initiazlied in the setup method.
100         self.__config = None
101         self.__configFileLocation = None
102         self.__imageCache = None
103         self.__shelf = None
104         self.__shelfLocation = None
105
106     def setup(self, configFileLocation=None, shelfLocation=None,
107               createMissingConfigFile=True, createMissingShelf=True):
108         """Set up the environment.
109
110         If configFileLocation is None, a per-system default value is
111         used.
112
113         If shelfLocation is None, a per-system default value is used.
114
115         A missing configuration file will be created iff
116         createMissingConfigFile is true.
117
118         A missing shelf will be created iff createMissingShelf is true.
119         """
120
121         if configFileLocation == None:
122             self.__configFileLocation = expanduser(DEFAULT_CONFIGFILE_LOCATION)
123         else:
124             self.__configFileLocation = configFileLocation
125
126         if not os.path.exists(self.configFileLocation):
127             confdir = os.path.dirname(self.configFileLocation)
128             if confdir and not os.path.exists(confdir):
129                 os.mkdir(confdir)
130                 self._writeInfo(u"Created directory \"%s\".\n" % confdir)
131             if createMissingConfigFile:
132                 f = codecs.open(
133                     self.configFileLocation, "w", self.localeEncoding)
134                 createConfigTemplate(f)
135                 self._writeInfo(u"Created configuration file \"%s\".\n" %
136                                 self.configFileLocation)
137             else:
138                 raise MissingConfigFileError(
139                     u"Missing configuration file: \"%s\"\n" %
140                          self.configFileLocation,
141                     self.configFileLocation)
142         self.__config = Config(self.localeEncoding)
143
144         try:
145             self.config.read(self.configFileLocation)
146             self.config.verify()
147         except MissingSectionHeaderError:
148             raise BadConfigFileError(
149                   "Bad configuration (missing section headers).\n",
150                   self.configFileLocation)
151         except MissingConfigurationKeyError, (section, key):
152             raise BadConfigFileError(
153                   "Missing configuration key in %s section: %s.\n" % (
154                       section, key),
155                   self.configFileLocation)
156         except BadConfigurationValueError, (section, key, value):
157             raise BadConfigFileError(
158                   "Bad configuration value for %s in %s section: %s.\n" % (
159                       key, section, value),
160                   self.configFileLocation)
161
162         if shelfLocation == None:
163             location = self.config.get("database", "location")
164             self.__shelfLocation = expanduser(location)
165         else:
166             self.__shelfLocation = shelfLocation
167
168         self.__shelf = Shelf(self.shelfLocation)
169
170         if not os.path.exists(self.shelfLocation):
171             if createMissingShelf:
172                 try:
173                     self.shelf.create()
174                 except FailedWritingError:
175                     raise BadShelfError(
176                         "Could not create metadata database \"%s\".\n" % (
177                             self.shelfLocation),
178                         self.shelfLocation)
179                 self._writeInfo(
180                     "Created metadata database \"%s\".\n" % self.shelfLocation)
181             else:
182                 raise MissingShelfError(
183                     "Could not open metadata database \"%s\"" % (
184                         self.shelfLocation),
185                     self.shelfLocation)
186
187         self.__imageCache = ImageCache(
188             expanduser(self.config.get("image cache", "location")),
189             self.config.getboolean("image cache", "use_orientation_attribute"))
190
191     @property
192     def localeEncoding(self):
193         """Get encoding of the locale."""
194         return self.__localeEncoding
195
196     @property
197     def filesystemEncoding(self):
198         """Get encoding of the filesystem."""
199         return self.__filesystemEncoding
200
201     @property
202     def config(self):
203         """Get the Config instance."""
204         return self.__config
205
206     @property
207     def configFileLocation(self):
208         """Get the configuration file location."""
209         return self.__configFileLocation
210
211     @property
212     def shelf(self):
213         """Get the Shelf instance."""
214         return self.__shelf
215
216     @property
217     def shelfLocation(self):
218         """Get the shelf location."""
219         return self.__shelfLocation
220
221     @property
222     def imageCache(self):
223         """Get the ImageCache instance."""
224         return self.__imageCache
225
226     @property
227     def version(self):
228         """Get the Kofoto version (a string)."""
229         return kofotoVersion
230
231     def _writeInfo(self, infoString):
232         """Write an informational string to a suitable place.
233
234         Should be overridden by subclasses.
235         """
236         raise NotImplementedError