Skip to content

Commit e7fea91

Browse files
committed
convert NS* types to built-in python types
1 parent 76293b3 commit e7fea91

File tree

5 files changed

+58
-34
lines changed

5 files changed

+58
-34
lines changed

README.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ implemented using the Objective C
1515
as well as the `launchd` command line utility. Therefore, this python package
1616
can only be used on `OS X <http://en.wikipedia.org/wiki/OS_X>`_
1717

18+
The python objective C bridge contains some special types. This package strips
19+
off all non built-in type information and returns pure python data.
20+
1821
Examples
1922
========
2023

launchd/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22

33
from .launchctl import jobs, LaunchdJob, load, unload, start, stop
44
from . import plist
5+
from . import util

launchd/launchctl.py

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from .cmd import launchctl
66
from .plist import discover_filename
7+
from .util import convert_NSDictionary_to_dict
78

89

910
class LaunchdJob(object):
@@ -54,40 +55,37 @@ def properties(self):
5455
'''
5556
This is a lazily loaded dictionary containing the launchd runtime
5657
information of the job in question. Internally, this is retrieved
57-
using `launchctl -x LABEL`. Keep in mind that some dictionary keys
58-
are not always present (for example 'PID').
58+
using ServiceManagement.SMJobCopyDictionary(). Keep in mind that
59+
some dictionary keys are not always present (for example 'PID').
5960
If the job specified by the label cannot be found in launchd, then
6061
this method raises a ValueError exception.
6162
'''
63+
if hasattr(self, '_nsproperties'):
64+
self._properties = convert_NSDictionary_to_dict(self._nsproperties)
65+
del self._nsproperties
66+
#self._nsproperties = None
6267
if self._properties is None:
6368
self.refresh()
6469
return self._properties
6570

6671
def exists(self):
67-
from subprocess import CalledProcessError
68-
try:
69-
_ = job_properties(self.label)
70-
except (CalledProcessError, ValueError):
71-
return False
72-
else:
73-
return True
72+
return ServiceManagement.SMJobCopyDictionary(None, self.label) != None
7473

7574
def refresh(self):
76-
from subprocess import CalledProcessError
77-
try:
78-
self._properties = job_properties(self.label)
79-
except (CalledProcessError, ValueError):
75+
val = ServiceManagement.SMJobCopyDictionary(None, self.label)
76+
if val is None:
8077
self._reset()
81-
raise ValueError("The job ('%s') does not exist!" % self.label)
78+
raise ValueError("job '%s' does not exist" % self.label)
8279
else:
80+
self._properties = convert_NSDictionary_to_dict(val)
8381
# update pid and laststatus attributes
84-
if 'PID' in self._properties:
82+
try:
8583
self._pid = self._properties['PID']
86-
else:
84+
except KeyError:
8785
self._pid = None
88-
if 'LastExitStatus' in self._properties:
86+
try:
8987
self._laststatus = self._properties['LastExitStatus']
90-
else:
88+
except KeyError:
9189
self._laststatus = None
9290

9391
@property
@@ -101,28 +99,22 @@ def plistfilename(self):
10199
return self._plist_fname
102100

103101

104-
def job_properties(joblabel):
105-
val = ServiceManagement.SMJobCopyDictionary(None, joblabel)
106-
if val is None:
107-
raise ValueError("job %s does not exist" % joblabel)
108-
return dict(val)
109-
110-
111102
def jobs():
112103
for entry in ServiceManagement.SMCopyAllJobDictionaries(None):
113-
if entry['Label'].startswith("0x"):
114-
continue
115104
label = entry['Label']
116-
if 'PID' in entry:
105+
if label.startswith("0x"):
106+
continue
107+
try:
117108
pid = int(entry['PID'])
118-
else:
109+
except KeyError:
119110
pid = None
120-
if 'LastExitStatus' in entry:
111+
try:
121112
status = int(entry['LastExitStatus'])
122-
else:
113+
except KeyError:
123114
status = None
124-
yield LaunchdJob(label, pid, status)
125-
115+
job = LaunchdJob(label, pid, status)
116+
job._nsproperties = entry
117+
yield job
126118

127119

128120
def start():

launchd/tests/launchctl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def test_launchd_jobs(self):
4646
self.assertTrue(isinstance(job, launchd.LaunchdJob))
4747
self.assertTrue(job.pid is None or isinstance(job.pid, int))
4848
self.assertTrue(job.laststatus is None or isinstance(job.laststatus, int))
49-
self.assertTrue(isinstance(job.properties, dict))
49+
self.assertTrue(isinstance(job.properties, dict), "props is not a dict: %s" % (type(job.properties)))
5050
self.assertTrue(job.plistfilename is None or isinstance(job.plistfilename, six.string_types))
5151
# the next 2 fail sometimes due to short lived processes that
5252
# have disappeared by the time we reach this test

launchd/util.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from Foundation import NSDictionary, NSArray
4+
from objc._pythonify import OC_PythonLong, OC_PythonFloat
5+
from objc import pyobjc_unicode
6+
7+
8+
def convert_NS_to_python(val):
9+
if isinstance(val, (pyobjc_unicode, str)):
10+
return str(val)
11+
elif isinstance(val, (OC_PythonLong, int)):
12+
return int(val)
13+
elif isinstance(val, (NSDictionary, dict)):
14+
return convert_NSDictionary_to_dict(val)
15+
elif isinstance(val, (NSArray, list, tuple)):
16+
return convert_NSArray_to_tuple(val)
17+
elif isinstance(val, (OC_PythonFloat,)):
18+
return float(val)
19+
else:
20+
raise TypeError("Unknown type '%s': '%r'!" % (str(type(val)), repr(val)))
21+
22+
23+
def convert_NSArray_to_tuple(nsarray):
24+
return ((convert_NS_to_python(val) for val in nsarray),)
25+
26+
27+
def convert_NSDictionary_to_dict(nsdict):
28+
return {convert_NS_to_python(k): convert_NS_to_python(nsdict[k]) for k in nsdict}

0 commit comments

Comments
 (0)