c_libraries_interface.py 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296
  1. # -*- coding: utf-8 -*-
  2. """
  3. Fast and exit-safe interface to GRASS C-library functions
  4. using ctypes and multiprocessing
  5. (C) 2013 by the GRASS Development Team
  6. This program is free software under the GNU General Public
  7. License (>=v2). Read the file COPYING that comes with GRASS
  8. for details.
  9. :authors: Soeren Gebbert
  10. """
  11. import sys
  12. from multiprocessing import Process, Lock, Pipe
  13. import logging
  14. from ctypes import *
  15. from core import *
  16. import grass.lib.gis as libgis
  17. import grass.lib.raster as libraster
  18. import grass.lib.vector as libvector
  19. import grass.lib.date as libdate
  20. import grass.lib.raster3d as libraster3d
  21. import grass.lib.temporal as libtgis
  22. ###############################################################################
  23. class RPCDefs(object):
  24. # Function identifier and index
  25. STOP = 0
  26. HAS_TIMESTAMP = 1
  27. WRITE_TIMESTAMP = 2
  28. READ_TIMESTAMP = 3
  29. REMOVE_TIMESTAMP = 4
  30. READ_MAP_INFO = 5
  31. MAP_EXISTS = 6
  32. READ_MAP_INFO = 7
  33. AVAILABLE_MAPSETS = 8
  34. GET_DRIVER_NAME = 9
  35. GET_DATABASE_NAME = 10
  36. G_MAPSET = 11
  37. G_LOCATION = 12
  38. G_GISDBASE = 13
  39. G_FATAL_ERROR = 14
  40. TYPE_RASTER = 0
  41. TYPE_RASTER3D = 1
  42. TYPE_VECTOR = 2
  43. ###############################################################################
  44. def _fatal_error(lock, conn, data):
  45. """Calls G_fatal_error()"""
  46. libgis.G_fatal_error("Fatal Error in C library server")
  47. def _get_mapset(lock, conn, data):
  48. """Return the current mapset
  49. :param lock: A multiprocessing.Lock instance
  50. :param conn: A multiprocessing.Pipe instance used to send True or False
  51. :param data: The mapset as list entry 1 [function_id]
  52. :returns: Name of the current mapset
  53. """
  54. mapset = libgis.G_mapset()
  55. conn.send(mapset)
  56. def _get_location(lock, conn, data):
  57. """Return the current location
  58. :param lock: A multiprocessing.Lock instance
  59. :param conn: A multiprocessing.Pipe instance used to send True or False
  60. :param data: The mapset as list entry 1 [function_id]
  61. :returns: Name of the location
  62. """
  63. location = libgis.G_location()
  64. conn.send(location)
  65. def _get_gisdbase(lock, conn, data):
  66. """Return the current gisdatabase
  67. :param lock: A multiprocessing.Lock instance
  68. :param conn: A multiprocessing.Pipe instance used to send True or False
  69. :param data: The mapset as list entry 1 [function_id]
  70. :returns: Name of the gisdatabase
  71. """
  72. gisdbase = libgis.G_gisdbase()
  73. conn.send(gisdbase)
  74. def _get_driver_name(lock, conn, data):
  75. """Return the temporal database driver of a specific mapset
  76. :param lock: A multiprocessing.Lock instance
  77. :param conn: A multiprocessing.Pipe instance used to send True or False
  78. :param data: The mapset as list entry 1 [function_id, mapset]
  79. :returns: Name of the driver or None if no temporal database present
  80. """
  81. mapset = data[1]
  82. if not mapset:
  83. mapset = libgis.G_mapset()
  84. drstring = libtgis.tgis_get_mapset_driver_name(mapset)
  85. conn.send(drstring)
  86. ###############################################################################
  87. def _get_database_name(lock, conn, data):
  88. """Return the temporal database name of a specific mapset
  89. :param lock: A multiprocessing.Lock instance
  90. :param conn: A multiprocessing.Pipe instance used to send True or False
  91. :param data: The mapset as list entry 1 [function_id, mapset]
  92. :returns: Name of the database or None if no temporal database present
  93. """
  94. mapset = data[1]
  95. if not mapset:
  96. mapset = libgis.G_mapset()
  97. dbstring = libtgis.tgis_get_mapset_database_name(mapset)
  98. if dbstring:
  99. # We substitute GRASS variables if they are located in the database string
  100. # This behavior is in conjunction with db.connect
  101. dbstring = dbstring.replace("$GISDBASE", libgis.G_gisdbase())
  102. dbstring = dbstring.replace("$LOCATION_NAME", libgis.G_location())
  103. dbstring = dbstring.replace("$MAPSET", libgis.G_mapset())
  104. conn.send(dbstring)
  105. ###############################################################################
  106. def _available_mapsets(lock, conn, data):
  107. """Return all available mapsets the user can access as a list of strings
  108. :param lock: A multiprocessing.Lock instance
  109. :param conn: A multiprocessing.Pipe instance used to send True or False
  110. :param data: The list of data entries [function_id]
  111. :returns: Names of available mapsets as list of strings
  112. """
  113. mapsets = libgis.G_get_available_mapsets()
  114. count = 0
  115. mapset_list = []
  116. while mapsets[count]:
  117. char_list = ""
  118. mapset = mapsets[count]
  119. if libgis.G_mapset_permissions(mapset) > 0:
  120. count += 1
  121. c = 0
  122. while mapset[c] != "\x00":
  123. char_list += mapset[c]
  124. c += 1
  125. mapset_list.append(char_list)
  126. # We need to sort the mapset list, but the first one should be
  127. # the current mapset
  128. current_mapset = libgis.G_mapset()
  129. mapset_list.remove(current_mapset)
  130. mapset_list.sort()
  131. mapset_list.reverse()
  132. mapset_list.append(current_mapset)
  133. mapset_list.reverse()
  134. conn.send(mapset_list)
  135. def _has_timestamp(lock, conn, data):
  136. """Check if the file based GRASS timestamp is present and send
  137. True or False using the provided pipe.
  138. :param lock: A multiprocessing.Lock instance
  139. :param conn: A multiprocessing.Pipe instance used to send True or False
  140. :param data: The list of data entries [function_id, maptype, name,
  141. mapset, layer]
  142. """
  143. maptype = data[1]
  144. name = data[2]
  145. mapset = data[3]
  146. layer = data[4]
  147. check = False
  148. if maptype == RPCDefs.TYPE_RASTER:
  149. if libgis.G_has_raster_timestamp(name, mapset) == 1:
  150. check = True
  151. elif maptype == RPCDefs.TYPE_VECTOR:
  152. if libgis.G_has_vector_timestamp(name, layer, mapset) == 1:
  153. check = True
  154. elif maptype == RPCDefs.TYPE_RASTER3D:
  155. if libgis.G_has_raster3d_timestamp(name, mapset) == 1:
  156. check = True
  157. conn.send(check)
  158. ###############################################################################
  159. def _read_timestamp(lock, conn, data):
  160. """Read the file based GRASS timestamp and send
  161. the result using the provided pipe.
  162. The tuple to be send via pipe: (return value of G_read_*_timestamp,
  163. timestamps).
  164. Please have a look at the documentation of G_read_raster_timestamp,
  165. G_read_vector_timestamp and G_read_raster3d_timestamp for the return
  166. values description.
  167. The timestamps to be send are tuples of values:
  168. - relative time (start, end, unit), start and end are of type
  169. integer, unit is of type string.
  170. - absolute time (start, end), start and end are of type datetime
  171. The end time may be None in case of a time instance.
  172. :param lock: A multiprocessing.Lock instance
  173. :param conn: A multiprocessing.Pipe instance used to send the result
  174. :param data: The list of data entries [function_id, maptype, name,
  175. mapset, layer]
  176. """
  177. maptype = data[1]
  178. name = data[2]
  179. mapset = data[3]
  180. layer = data[4]
  181. check = False
  182. ts = libgis.TimeStamp()
  183. if maptype == RPCDefs.TYPE_RASTER:
  184. check = libgis.G_read_raster_timestamp(name, mapset, byref(ts))
  185. elif maptype == RPCDefs.TYPE_VECTOR:
  186. check = libgis.G_read_vector_timestamp(name, layer, mapset, byref(ts))
  187. elif maptype == RPCDefs.TYPE_RASTER3D:
  188. check = libgis.G_read_raster3d_timestamp(name, mapset, byref(ts))
  189. dates = _convert_timestamp_from_grass(ts)
  190. conn.send((check, dates))
  191. ###############################################################################
  192. def _write_timestamp(lock, conn, data):
  193. """Write the file based GRASS timestamp
  194. the return values of the called C-functions using the provided pipe.
  195. The value to be send via pipe is the return value of G_write_*_timestamp.
  196. Please have a look at the documentation of G_write_raster_timestamp,
  197. G_write_vector_timestamp and G_write_raster3d_timestamp for the return
  198. values description.
  199. :param lock: A multiprocessing.Lock instance
  200. :param conn: A multiprocessing.Pipe instance used to send True or False
  201. :param data: The list of data entries [function_id, maptype, name,
  202. mapset, layer, timestring]
  203. """
  204. maptype = data[1]
  205. name = data[2]
  206. mapset = data[3]
  207. layer = data[4]
  208. timestring = data[5]
  209. check = -3
  210. ts = libgis.TimeStamp()
  211. check = libgis.G_scan_timestamp(byref(ts), timestring)
  212. if check != 1:
  213. logging.error("Unable to convert the timestamp: " + timestring)
  214. return -2
  215. if maptype == RPCDefs.TYPE_RASTER:
  216. check = libgis.G_write_raster_timestamp(name, byref(ts))
  217. elif maptype == RPCDefs.TYPE_VECTOR:
  218. check = libgis.G_write_vector_timestamp(name, layer, byref(ts))
  219. elif maptype == RPCDefs.TYPE_RASTER3D:
  220. check = libgis.G_write_raster3d_timestamp(name, byref(ts))
  221. conn.send(check)
  222. ###############################################################################
  223. def _remove_timestamp(lock, conn, data):
  224. """Remove the file based GRASS timestamp
  225. the return values of the called C-functions using the provided pipe.
  226. The value to be send via pipe is the return value of G_remove_*_timestamp.
  227. Please have a look at the documentation of G_remove_raster_timestamp,
  228. G_remove_vector_timestamp and G_remove_raster3d_timestamp for the return
  229. values description.
  230. :param lock: A multiprocessing.Lock instance
  231. :param conn: A multiprocessing.Pipe instance used to send True or False
  232. :param data: The list of data entries [function_id, maptype, name,
  233. mapset, layer]
  234. """
  235. maptype = data[1]
  236. name = data[2]
  237. mapset = data[3]
  238. layer = data[4]
  239. check = False
  240. if maptype == RPCDefs.TYPE_RASTER:
  241. check = libgis.G_remove_raster_timestamp(name, mapset)
  242. elif maptype == RPCDefs.TYPE_VECTOR:
  243. check = libgis.G_remove_vector_timestamp(name, layer, mapset)
  244. elif maptype == RPCDefs.TYPE_RASTER3D:
  245. check = libgis.G_remove_raster3d_timestamp(name, mapset)
  246. conn.send(check)
  247. ###############################################################################
  248. def _map_exists(lock, conn, data):
  249. """Check if a map exists in the spatial database
  250. The value to be send via pipe is True in case the map exists and False
  251. if not.
  252. :param lock: A multiprocessing.Lock instance
  253. :param conn: A multiprocessing.Pipe instance used to send True or False
  254. :param data: The list of data entries [function_id, maptype, name, mapset]
  255. """
  256. maptype = data[1]
  257. name = data[2]
  258. mapset = data[3]
  259. check = False
  260. if maptype == RPCDefs.TYPE_RASTER:
  261. mapset = libgis.G_find_raster(name, mapset)
  262. elif maptype == RPCDefs.TYPE_VECTOR:
  263. mapset = libgis.G_find_vector(name, mapset)
  264. elif maptype == RPCDefs.TYPE_RASTER3D:
  265. mapset = libgis.G_find_raster3d(name, mapset)
  266. if mapset:
  267. check = True
  268. conn.send(check)
  269. ###############################################################################
  270. def _read_map_info(lock, conn, data):
  271. """Read map specific metadata from the spatial database using C-library
  272. functions
  273. :param lock: A multiprocessing.Lock instance
  274. :param conn: A multiprocessing.Pipe instance used to send True or False
  275. :param data: The list of data entries [function_id, maptype, name, mapset]
  276. """
  277. maptype = data[1]
  278. name = data[2]
  279. mapset = data[3]
  280. if maptype == RPCDefs.TYPE_RASTER:
  281. kvp = _read_raster_info(name, mapset)
  282. elif maptype == RPCDefs.TYPE_VECTOR:
  283. kvp = _read_vector_info(name, mapset)
  284. elif maptype == RPCDefs.TYPE_RASTER3D:
  285. kvp = _read_raster3d_info(name, mapset)
  286. conn.send(kvp)
  287. ###############################################################################
  288. def _read_raster_info(name, mapset):
  289. """Read the raster map info from the file system and store the content
  290. into a dictionary
  291. This method uses the ctypes interface to the gis and raster libraries
  292. to read the map metadata information
  293. :param name: The name of the map
  294. :param mapset: The mapset of the map
  295. :returns: The key value pairs of the map specific metadata, or None in
  296. case of an error
  297. """
  298. kvp = {}
  299. if not libgis.G_find_raster(name, mapset):
  300. return None
  301. # Read the region information
  302. region = libgis.Cell_head()
  303. libraster.Rast_get_cellhd(name, mapset, byref(region))
  304. kvp["north"] = region.north
  305. kvp["south"] = region.south
  306. kvp["east"] = region.east
  307. kvp["west"] = region.west
  308. kvp["nsres"] = region.ns_res
  309. kvp["ewres"] = region.ew_res
  310. kvp["rows"] = region.cols
  311. kvp["cols"] = region.rows
  312. maptype = libraster.Rast_map_type(name, mapset)
  313. if maptype == libraster.DCELL_TYPE:
  314. kvp["datatype"] = "DCELL"
  315. elif maptype == libraster.FCELL_TYPE:
  316. kvp["datatype"] = "FCELL"
  317. elif maptype == libraster.CELL_TYPE:
  318. kvp["datatype"] = "CELL"
  319. # Read range
  320. if libraster.Rast_map_is_fp(name, mapset):
  321. range = libraster.FPRange()
  322. libraster.Rast_init_fp_range(byref(range))
  323. ret = libraster.Rast_read_fp_range(name, mapset, byref(range))
  324. if ret < 0:
  325. logging.error(_("Unable to read range file"))
  326. return None
  327. if ret == 2:
  328. kvp["min"] = None
  329. kvp["max"] = None
  330. else:
  331. min = libgis.DCELL()
  332. max = libgis.DCELL()
  333. libraster.Rast_get_fp_range_min_max(
  334. byref(range), byref(min), byref(max))
  335. kvp["min"] = min.value
  336. kvp["max"] = max.value
  337. else:
  338. range = libraster.Range()
  339. libraster.Rast_init_range(byref(range))
  340. ret = libraster.Rast_read_range(name, mapset, byref(range))
  341. if ret < 0:
  342. logging.error(_("Unable to read range file"))
  343. return None
  344. if ret == 2:
  345. kvp["min"] = None
  346. kvp["max"] = None
  347. else:
  348. min = libgis.CELL()
  349. max = libgis.CELL()
  350. libraster.Rast_get_range_min_max(
  351. byref(range), byref(min), byref(max))
  352. kvp["min"] = min.value
  353. kvp["max"] = max.value
  354. return kvp
  355. ###############################################################################
  356. def _read_raster3d_info(name, mapset):
  357. """Read the 3D raster map info from the file system and store the content
  358. into a dictionary
  359. This method uses the ctypes interface to the gis and raster3d libraries
  360. to read the map metadata information
  361. :param name: The name of the map
  362. :param mapset: The mapset of the map
  363. :returns: The key value pairs of the map specific metadata, or None in
  364. case of an error
  365. """
  366. kvp = {}
  367. if not libgis.G_find_raster3d(name, mapset):
  368. return None
  369. # Read the region information
  370. region = libraster3d.RASTER3D_Region()
  371. libraster3d.Rast3d_read_region_map(name, mapset, byref(region))
  372. kvp["north"] = region.north
  373. kvp["south"] = region.south
  374. kvp["east"] = region.east
  375. kvp["west"] = region.west
  376. kvp["nsres"] = region.ns_res
  377. kvp["ewres"] = region.ew_res
  378. kvp["tbres"] = region.tb_res
  379. kvp["rows"] = region.cols
  380. kvp["cols"] = region.rows
  381. kvp["depths"] = region.depths
  382. kvp["top"] = region.top
  383. kvp["bottom"] = region.bottom
  384. # We need to open the map, this function returns a void pointer
  385. # but we may need the correct type which is RASTER3D_Map, hence
  386. # the casting
  387. g3map = cast(libraster3d.Rast3d_open_cell_old(name, mapset,
  388. libraster3d.RASTER3D_DEFAULT_WINDOW,
  389. libraster3d.RASTER3D_TILE_SAME_AS_FILE,
  390. libraster3d.RASTER3D_NO_CACHE),
  391. POINTER(libraster3d.RASTER3D_Map))
  392. if not g3map:
  393. logging.error(_("Unable to open 3D raster map <%s>" % (name)))
  394. return None
  395. maptype = libraster3d.Rast3d_file_type_map(g3map)
  396. if maptype == libraster.DCELL_TYPE:
  397. kvp["datatype"] = "DCELL"
  398. elif maptype == libraster.FCELL_TYPE:
  399. kvp["datatype"] = "FCELL"
  400. # Read range
  401. min = libgis.DCELL()
  402. max = libgis.DCELL()
  403. ret = libraster3d.Rast3d_range_load(g3map)
  404. if not ret:
  405. logging.error(_("Unable to load range of 3D raster map <%s>" %
  406. (name)))
  407. return None
  408. libraster3d.Rast3d_range_min_max(g3map, byref(min), byref(max))
  409. if min.value != min.value:
  410. kvp["min"] = None
  411. else:
  412. kvp["min"] = float(min.value)
  413. if max.value != max.value:
  414. kvp["max"] = None
  415. else:
  416. kvp["max"] = float(max.value)
  417. if not libraster3d.Rast3d_close(g3map):
  418. logging.error(_("Unable to close 3D raster map <%s>" % (name)))
  419. return None
  420. return kvp
  421. ###############################################################################
  422. def _read_vector_info(name, mapset):
  423. """Read the vector map info from the file system and store the content
  424. into a dictionary
  425. This method uses the ctypes interface to the vector libraries
  426. to read the map metadata information
  427. :param name: The name of the map
  428. :param mapset: The mapset of the map
  429. :returns: The key value pairs of the map specific metadata, or None in
  430. case of an error
  431. """
  432. kvp = {}
  433. if not libgis.G_find_vector(name, mapset):
  434. return None
  435. # The vector map structure
  436. Map = libvector.Map_info()
  437. # We open the maps always in topology mode first
  438. libvector.Vect_set_open_level(2)
  439. with_topo = True
  440. # Code lend from v.info main.c
  441. if libvector.Vect_open_old_head2(byref(Map), name, mapset, "1") < 2:
  442. # force level 1, open fully
  443. # NOTE: number of points, lines, boundaries, centroids,
  444. # faces, kernels is still available
  445. libvector.Vect_set_open_level(1) # no topology
  446. with_topo = False
  447. if libvector.Vect_open_old2(byref(Map), name, mapset, "1") < 1:
  448. logging.error(_("Unable to open vector map <%s>" %
  449. (libvector.Vect_get_full_name(byref(Map)))))
  450. return None
  451. # Release the vector spatial index memory when closed
  452. libvector.Vect_set_release_support(byref(Map))
  453. # Read the extent information
  454. bbox = libvector.bound_box()
  455. libvector.Vect_get_map_box(byref(Map), byref(bbox))
  456. kvp["north"] = bbox.N
  457. kvp["south"] = bbox.S
  458. kvp["east"] = bbox.E
  459. kvp["west"] = bbox.W
  460. kvp["top"] = bbox.T
  461. kvp["bottom"] = bbox.B
  462. kvp["map3d"] = bool(libvector.Vect_is_3d(byref(Map)))
  463. # Read number of features
  464. if with_topo:
  465. kvp["points"] = libvector.Vect_get_num_primitives(
  466. byref(Map), libvector.GV_POINT)
  467. kvp["lines"] = libvector.Vect_get_num_primitives(
  468. byref(Map), libvector.GV_LINE)
  469. kvp["boundaries"] = libvector.Vect_get_num_primitives(
  470. byref(Map), libvector.GV_BOUNDARY)
  471. kvp["centroids"] = libvector.Vect_get_num_primitives(
  472. byref(Map), libvector.GV_CENTROID)
  473. kvp["faces"] = libvector.Vect_get_num_primitives(
  474. byref(Map), libvector.GV_FACE)
  475. kvp["kernels"] = libvector.Vect_get_num_primitives(
  476. byref(Map), libvector.GV_KERNEL)
  477. # Summarize the primitives
  478. kvp["primitives"] = kvp["points"] + kvp["lines"] + \
  479. kvp["boundaries"] + kvp["centroids"]
  480. if kvp["map3d"]:
  481. kvp["primitives"] += kvp["faces"] + kvp["kernels"]
  482. # Read topology information
  483. kvp["nodes"] = libvector.Vect_get_num_nodes(byref(Map))
  484. kvp["areas"] = libvector.Vect_get_num_areas(byref(Map))
  485. kvp["islands"] = libvector.Vect_get_num_islands(byref(Map))
  486. kvp["holes"] = libvector.Vect_get_num_holes(byref(Map))
  487. kvp["volumes"] = libvector.Vect_get_num_primitives(
  488. byref(Map), libvector.GV_VOLUME)
  489. else:
  490. kvp["points"] = None
  491. kvp["lines"] = None
  492. kvp["boundaries"] = None
  493. kvp["centroids"] = None
  494. kvp["faces"] = None
  495. kvp["kernels"] = None
  496. kvp["primitives"] = None
  497. kvp["nodes"] = None
  498. kvp["areas"] = None
  499. kvp["islands"] = None
  500. kvp["holes"] = None
  501. kvp["volumes"] = None
  502. libvector.Vect_close(byref(Map))
  503. return kvp
  504. ###############################################################################
  505. def _convert_timestamp_from_grass(ts):
  506. """Convert a GRASS file based timestamp into the temporal framework
  507. format datetime or integer.
  508. A tuple of two datetime objects (start, end) is returned in case of
  509. absolute time.
  510. In case of relative time a tuple with start time, end time and the
  511. relative unit (start, end, unit) will be returned.
  512. Note:
  513. The end time will be set to None in case of a time instance.
  514. :param ts grass.lib.gis.TimeStamp object created by G_read_*_timestamp
  515. """
  516. dt1 = libgis.DateTime()
  517. dt2 = libgis.DateTime()
  518. count = c_int()
  519. libgis.G_get_timestamps(byref(ts),
  520. byref(dt1),
  521. byref(dt2),
  522. byref(count))
  523. if dt1.mode == libdate.DATETIME_ABSOLUTE:
  524. pdt1 = None
  525. pdt2 = None
  526. if count.value >= 1:
  527. pdt1 = datetime(int(dt1.year), int(dt1.month), int(dt1.day),
  528. int(dt1.hour), int(dt1.minute),
  529. int(dt1.second))
  530. if count.value == 2:
  531. pdt2 = datetime(int(dt2.year), int(dt2.month), int(dt2.day),
  532. int(dt2.hour), int(dt2.minute),
  533. int(dt2.second))
  534. # ATTENTION: We ignore the time zone
  535. # TODO: Write time zone support
  536. return (pdt1, pdt2)
  537. else:
  538. unit = None
  539. start = None
  540. end = None
  541. if count.value >= 1:
  542. if dt1.year > 0:
  543. unit = "years"
  544. start = dt1.year
  545. elif dt1.month > 0:
  546. unit = "months"
  547. start = dt1.month
  548. elif dt1.day > 0:
  549. unit = "days"
  550. start = dt1.day
  551. elif dt1.hour > 0:
  552. unit = "hours"
  553. start = dt1.hour
  554. elif dt1.minute > 0:
  555. unit = "minutes"
  556. start = dt1.minute
  557. elif dt1.second > 0:
  558. unit = "seconds"
  559. start = dt1.second
  560. if count.value == 2:
  561. if dt2.year > 0:
  562. end = dt2.year
  563. elif dt2.month > 0:
  564. end = dt2.month
  565. elif dt2.day > 0:
  566. end = dt2.day
  567. elif dt2.hour > 0:
  568. end = dt2.hour
  569. elif dt2.minute > 0:
  570. end = dt2.minute
  571. elif dt2.second > 0:
  572. end = dt2.second
  573. return (start, end, unit)
  574. ###############################################################################
  575. def _stop(lock, conn, data):
  576. libgis.G_debug(1, "Stop C-interface server")
  577. conn.close()
  578. lock.release()
  579. sys.exit()
  580. ###############################################################################
  581. # Global server connection
  582. server_connection = None
  583. server_lock = None
  584. def c_library_server(lock, conn):
  585. """The GRASS C-libraries server function designed to be a target for
  586. multiprocessing.Process
  587. :param lock: A multiprocessing.Lock
  588. :param conn: A multiprocessing.Pipe
  589. """
  590. # Crerate the function array
  591. functions = [0]*15
  592. functions[RPCDefs.STOP] = _stop
  593. functions[RPCDefs.HAS_TIMESTAMP] = _has_timestamp
  594. functions[RPCDefs.WRITE_TIMESTAMP] = _write_timestamp
  595. functions[RPCDefs.READ_TIMESTAMP] = _read_timestamp
  596. functions[RPCDefs.REMOVE_TIMESTAMP] = _remove_timestamp
  597. functions[RPCDefs.READ_MAP_INFO] = _read_map_info
  598. functions[RPCDefs.MAP_EXISTS] = _map_exists
  599. functions[RPCDefs.AVAILABLE_MAPSETS] = _available_mapsets
  600. functions[RPCDefs.GET_DRIVER_NAME] = _get_driver_name
  601. functions[RPCDefs.GET_DATABASE_NAME] = _get_database_name
  602. functions[RPCDefs.G_MAPSET] = _get_mapset
  603. functions[RPCDefs.G_LOCATION] = _get_location
  604. functions[RPCDefs.G_GISDBASE] = _get_gisdbase
  605. functions[RPCDefs.G_FATAL_ERROR] = _fatal_error
  606. libgis.G_gisinit("c_library_server")
  607. libgis.G_debug(1, "Start C-interface server")
  608. while True:
  609. # Avoid busy waiting
  610. conn.poll(4)
  611. data = conn.recv()
  612. lock.acquire()
  613. functions[data[0]](lock, conn, data)
  614. lock.release()
  615. class CLibrariesInterface(object):
  616. """Fast and exit-safe interface to GRASS C-libraries functions
  617. This class implements a fast and exit-safe interface to the GRASS
  618. gis, raster, 3D raster and vector C-libraries functions.
  619. The C-libraries functions are called via ctypes in a subprocess
  620. using a pipe (multiprocessing.Pipe) to transfer the text messages.
  621. Hence, the process that uses the CLibrariesInterface will not be
  622. exited, if a G_fatal_error() was invoked in the subprocess.
  623. In this case the CLibrariesInterface object will simply start a
  624. new subprocess and restarts the pipeline.
  625. Usage:
  626. .. code-block:: python
  627. >>> import grass.script as gscript
  628. >>> import grass.temporal as tgis
  629. >>> gscript.use_temp_region()
  630. >>> gscript.run_command("g.region", n=80.0, s=0.0, e=120.0, w=0.0,
  631. ... t=1.0, b=0.0, res=10.0, res3=10.0)
  632. 0
  633. >>> tgis.init()
  634. >>> gscript.run_command("r.mapcalc", expression="test = 1", overwrite=True, quiet=True)
  635. 0
  636. >>> gscript.run_command("r3.mapcalc", expression="test = 1", overwrite=True, quiet=True)
  637. 0
  638. >>> gscript.run_command("v.random", output="test", n=10, overwrite=True, quiet=True)
  639. 0
  640. >>> gscript.run_command("r.timestamp", map="test", date='12 Mar 1995 10:34:40', overwrite=True, quiet=True)
  641. 0
  642. >>> gscript.run_command("r3.timestamp", map="test", date='12 Mar 1995 10:34:40', overwrite=True, quiet=True)
  643. 0
  644. >>> gscript.run_command("v.timestamp", map="test", date='12 Mar 1995 10:34:40', overwrite=True, quiet=True)
  645. 0
  646. # Check mapsets
  647. >>> ciface = tgis.CLibrariesInterface()
  648. >>> mapsets = ciface.available_mapsets()
  649. >>> mapsets[0] == tgis.get_current_mapset()
  650. True
  651. # Raster map
  652. >>> ciface = tgis.CLibrariesInterface()
  653. >>> check = ciface.raster_map_exists("test", tgis.get_current_mapset())
  654. >>> print check
  655. True
  656. >>> ciface.read_raster_info("test", tgis.get_current_mapset())
  657. {'rows': 12, 'north': 80.0, 'min': 1, 'datatype': 'CELL', 'max': 1, 'ewres': 10.0, 'cols': 8, 'west': 0.0, 'east': 120.0, 'nsres': 10.0, 'south': 0.0}
  658. >>> check = ciface.has_raster_timestamp("test", tgis.get_current_mapset())
  659. >>> print check
  660. True
  661. >>> if check:
  662. ... res = ciface.read_raster_timestamp("test", tgis.get_current_mapset())
  663. ... if res[0]:
  664. ... print str(res[1][0]), str(res[1][0])
  665. ... ciface.remove_raster_timestamp("test", tgis.get_current_mapset())
  666. 1995-03-12 10:34:40 1995-03-12 10:34:40
  667. 1
  668. >>> ciface.has_raster_timestamp("test", tgis.get_current_mapset())
  669. False
  670. >>> ciface.write_raster_timestamp("test", tgis.get_current_mapset(), "13 Jan 1999 14:30:05")
  671. 1
  672. >>> ciface.has_raster_timestamp("test", tgis.get_current_mapset())
  673. True
  674. # 3D raster map
  675. >>> check = ciface.raster3d_map_exists("test", tgis.get_current_mapset())
  676. >>> print check
  677. True
  678. >>> ciface.read_raster3d_info("test", tgis.get_current_mapset())
  679. {'tbres': 1.0, 'rows': 12, 'north': 80.0, 'bottom': 0.0, 'datatype': 'DCELL', 'max': 1.0, 'top': 1.0, 'min': 1.0, 'cols': 8, 'depths': 1, 'west': 0.0, 'ewres': 10.0, 'east': 120.0, 'nsres': 10.0, 'south': 0.0}
  680. >>> check = ciface.has_raster3d_timestamp("test", tgis.get_current_mapset())
  681. >>> print check
  682. True
  683. >>> if check:
  684. ... res = ciface.read_raster3d_timestamp("test", tgis.get_current_mapset())
  685. ... if res[0]:
  686. ... print str(res[1][0]), str(res[1][0])
  687. ... ciface.remove_raster3d_timestamp("test", tgis.get_current_mapset())
  688. 1995-03-12 10:34:40 1995-03-12 10:34:40
  689. 1
  690. >>> ciface.has_raster3d_timestamp("test", tgis.get_current_mapset())
  691. False
  692. >>> ciface.write_raster3d_timestamp("test", tgis.get_current_mapset(), "13 Jan 1999 14:30:05")
  693. 1
  694. >>> ciface.has_raster3d_timestamp("test", tgis.get_current_mapset())
  695. True
  696. # Vector map
  697. >>> check = ciface.vector_map_exists("test", tgis.get_current_mapset())
  698. >>> print check
  699. True
  700. >>> kvp = ciface.read_vector_info("test", tgis.get_current_mapset())
  701. >>> print kvp['points']
  702. 10
  703. >>> check = ciface.has_vector_timestamp("test", tgis.get_current_mapset(), None)
  704. >>> print check
  705. True
  706. >>> if check:
  707. ... res = ciface.read_vector_timestamp("test", tgis.get_current_mapset())
  708. ... if res[0]:
  709. ... print str(res[1][0]), str(res[1][0])
  710. ... ciface.remove_vector_timestamp("test", tgis.get_current_mapset())
  711. 1995-03-12 10:34:40 1995-03-12 10:34:40
  712. 1
  713. >>> ciface.has_vector_timestamp("test", tgis.get_current_mapset())
  714. False
  715. >>> ciface.write_vector_timestamp("test", tgis.get_current_mapset(), "13 Jan 1999 14:30:05")
  716. 1
  717. >>> ciface.has_vector_timestamp("test", tgis.get_current_mapset())
  718. True
  719. >>> ciface.get_driver_name()
  720. 'sqlite'
  721. >>> ciface.get_database_name().split("/")[-1]
  722. 'sqlite.db'
  723. >>> mapset = ciface.get_mapset()
  724. >>> location = ciface.get_location()
  725. >>> gisdbase = ciface.get_gisdbase()
  726. >>> gscript.del_temp_region()
  727. """
  728. def __init__(self):
  729. self.client_conn = None
  730. self.server_conn = None
  731. self.queue = None
  732. self.server = None
  733. self.start_server()
  734. def start_server(self):
  735. self.client_conn, self.server_conn = Pipe(True)
  736. self.lock = Lock()
  737. self.server = Process(target=c_library_server, args=(self.lock,
  738. self.server_conn))
  739. self.server.daemon = True
  740. self.server.start()
  741. def _check_restart_server(self):
  742. """Restart the server if it was terminated
  743. """
  744. if self.server.is_alive() is True:
  745. return
  746. self.client_conn.close()
  747. self.server_conn.close()
  748. self.start_server()
  749. logging.warning("Needed to restart the libgis server")
  750. def raster_map_exists(self, name, mapset):
  751. """Check if a raster map exists in the spatial database
  752. :param name: The name of the map
  753. :param mapset: The mapset of the map
  754. :returns: True if exists, False if not
  755. """
  756. self._check_restart_server()
  757. self.client_conn.send([RPCDefs.MAP_EXISTS, RPCDefs.TYPE_RASTER,
  758. name, mapset, None])
  759. return self.client_conn.recv()
  760. def read_raster_info(self, name, mapset):
  761. """Read the raster map info from the file system and store the content
  762. into a dictionary
  763. :param name: The name of the map
  764. :param mapset: The mapset of the map
  765. :returns: The key value pairs of the map specific metadata,
  766. or None in case of an error
  767. """
  768. self._check_restart_server()
  769. self.client_conn.send([RPCDefs.READ_MAP_INFO, RPCDefs.TYPE_RASTER,
  770. name, mapset, None])
  771. return self.client_conn.recv()
  772. def has_raster_timestamp(self, name, mapset):
  773. """Check if a file based raster timetamp exists
  774. :param name: The name of the map
  775. :param mapset: The mapset of the map
  776. :returns: True if exists, False if not
  777. """
  778. self._check_restart_server()
  779. self.client_conn.send([RPCDefs.HAS_TIMESTAMP, RPCDefs.TYPE_RASTER,
  780. name, mapset, None])
  781. return self.client_conn.recv()
  782. def remove_raster_timestamp(self, name, mapset):
  783. """Remove a file based raster timetamp
  784. Please have a look at the documentation G_remove_raster_timestamp
  785. for the return values description.
  786. :param name: The name of the map
  787. :param mapset: The mapset of the map
  788. :returns: The return value of G_remove_raster_timestamp
  789. """
  790. self._check_restart_server()
  791. self.client_conn.send([RPCDefs.REMOVE_TIMESTAMP, RPCDefs.TYPE_RASTER,
  792. name, mapset, None])
  793. return self.client_conn.recv()
  794. def read_raster_timestamp(self, name, mapset):
  795. """Read a file based raster timetamp
  796. Please have a look at the documentation G_read_raster_timestamp
  797. for the return values description.
  798. The timestamps to be send are tuples of values:
  799. - relative time (start, end, unit), start and end are of type
  800. integer, unit is of type string.
  801. - absolute time (start, end), start and end are of type datetime
  802. The end time may be None in case of a time instance.
  803. :param name: The name of the map
  804. :param mapset: The mapset of the map
  805. :returns: The return value of G_read_raster_timestamp
  806. """
  807. self._check_restart_server()
  808. self.client_conn.send([RPCDefs.READ_TIMESTAMP, RPCDefs.TYPE_RASTER,
  809. name, mapset, None])
  810. return self.client_conn.recv()
  811. def write_raster_timestamp(self, name, mapset, timestring):
  812. """Write a file based raster timetamp
  813. Please have a look at the documentation G_write_raster_timestamp
  814. for the return values description.
  815. Note:
  816. Only timestamps of maps from the current mapset can written.
  817. :param name: The name of the map
  818. :param mapset: The mapset of the map
  819. :param timestring: A GRASS datetime C-library compatible string
  820. :returns: The return value of G_write_raster_timestamp
  821. """
  822. self._check_restart_server()
  823. self.client_conn.send([RPCDefs.WRITE_TIMESTAMP, RPCDefs.TYPE_RASTER,
  824. name, mapset, None, timestring])
  825. return self.client_conn.recv()
  826. def raster3d_map_exists(self, name, mapset):
  827. """Check if a 3D raster map exists in the spatial database
  828. :param name: The name of the map
  829. :param mapset: The mapset of the map
  830. :returns: True if exists, False if not
  831. """
  832. self._check_restart_server()
  833. self.client_conn.send([RPCDefs.MAP_EXISTS, RPCDefs.TYPE_RASTER3D,
  834. name, mapset, None])
  835. return self.client_conn.recv()
  836. def read_raster3d_info(self, name, mapset):
  837. """Read the 3D raster map info from the file system and store the content
  838. into a dictionary
  839. :param name: The name of the map
  840. :param mapset: The mapset of the map
  841. :returns: The key value pairs of the map specific metadata,
  842. or None in case of an error
  843. """
  844. self._check_restart_server()
  845. self.client_conn.send([RPCDefs.READ_MAP_INFO, RPCDefs.TYPE_RASTER3D,
  846. name, mapset, None])
  847. return self.client_conn.recv()
  848. def has_raster3d_timestamp(self, name, mapset):
  849. """Check if a file based 3D raster timetamp exists
  850. :param name: The name of the map
  851. :param mapset: The mapset of the map
  852. :returns: True if exists, False if not
  853. """
  854. self._check_restart_server()
  855. self.client_conn.send([RPCDefs.HAS_TIMESTAMP, RPCDefs.TYPE_RASTER3D,
  856. name, mapset, None])
  857. return self.client_conn.recv()
  858. def remove_raster3d_timestamp(self, name, mapset):
  859. """Remove a file based 3D raster timetamp
  860. Please have a look at the documentation G_remove_raster3d_timestamp
  861. for the return values description.
  862. :param name: The name of the map
  863. :param mapset: The mapset of the map
  864. :returns: The return value of G_remove_raster3d_timestamp
  865. """
  866. self._check_restart_server()
  867. self.client_conn.send([RPCDefs.REMOVE_TIMESTAMP, RPCDefs.TYPE_RASTER3D,
  868. name, mapset, None])
  869. return self.client_conn.recv()
  870. def read_raster3d_timestamp(self, name, mapset):
  871. """Read a file based 3D raster timetamp
  872. Please have a look at the documentation G_read_raster3d_timestamp
  873. for the return values description.
  874. The timestamps to be send are tuples of values:
  875. - relative time (start, end, unit), start and end are of type
  876. integer, unit is of type string.
  877. - absolute time (start, end), start and end are of type datetime
  878. The end time may be None in case of a time instance.
  879. :param name: The name of the map
  880. :param mapset: The mapset of the map
  881. :returns: The return value of G_read_raster3d_timestamp
  882. """
  883. self._check_restart_server()
  884. self.client_conn.send([RPCDefs.READ_TIMESTAMP, RPCDefs.TYPE_RASTER3D,
  885. name, mapset, None])
  886. return self.client_conn.recv()
  887. def write_raster3d_timestamp(self, name, mapset, timestring):
  888. """Write a file based 3D raster timetamp
  889. Please have a look at the documentation G_write_raster3d_timestamp
  890. for the return values description.
  891. Note:
  892. Only timestamps of maps from the current mapset can written.
  893. :param name: The name of the map
  894. :param mapset: The mapset of the map
  895. :param timestring: A GRASS datetime C-library compatible string
  896. :returns: The return value of G_write_raster3d_timestamp
  897. """
  898. self._check_restart_server()
  899. self.client_conn.send([RPCDefs.WRITE_TIMESTAMP, RPCDefs.TYPE_RASTER3D,
  900. name, mapset, None, timestring])
  901. return self.client_conn.recv()
  902. def vector_map_exists(self, name, mapset):
  903. """Check if a vector map exists in the spatial database
  904. :param name: The name of the map
  905. :param mapset: The mapset of the map
  906. :returns: True if exists, False if not
  907. """
  908. self._check_restart_server()
  909. self.client_conn.send([RPCDefs.MAP_EXISTS, RPCDefs.TYPE_VECTOR,
  910. name, mapset, None])
  911. return self.client_conn.recv()
  912. def read_vector_info(self, name, mapset):
  913. """Read the vector map info from the file system and store the content
  914. into a dictionary
  915. :param name: The name of the map
  916. :param mapset: The mapset of the map
  917. :returns: The key value pairs of the map specific metadata,
  918. or None in case of an error
  919. """
  920. self._check_restart_server()
  921. self.client_conn.send([RPCDefs.READ_MAP_INFO, RPCDefs.TYPE_VECTOR,
  922. name, mapset, None])
  923. return self.client_conn.recv()
  924. def has_vector_timestamp(self, name, mapset, layer=None):
  925. """Check if a file based vector timetamp exists
  926. :param name: The name of the map
  927. :param mapset: The mapset of the map
  928. :param layer: The layer of the vector map
  929. :returns: True if exists, False if not
  930. """
  931. self._check_restart_server()
  932. self.client_conn.send([RPCDefs.HAS_TIMESTAMP, RPCDefs.TYPE_VECTOR,
  933. name, mapset, layer])
  934. return self.client_conn.recv()
  935. def remove_vector_timestamp(self, name, mapset, layer=None):
  936. """Remove a file based vector timetamp
  937. Please have a look at the documentation G_remove_vector_timestamp
  938. for the return values description.
  939. :param name: The name of the map
  940. :param mapset: The mapset of the map
  941. :param layer: The layer of the vector map
  942. :returns: The return value of G_remove_vector_timestamp
  943. """
  944. self._check_restart_server()
  945. self.client_conn.send([RPCDefs.REMOVE_TIMESTAMP, RPCDefs.TYPE_VECTOR,
  946. name, mapset, layer])
  947. return self.client_conn.recv()
  948. def read_vector_timestamp(self, name, mapset, layer=None):
  949. """Read a file based vector timetamp
  950. Please have a look at the documentation G_read_vector_timestamp
  951. for the return values description.
  952. The timestamps to be send are tuples of values:
  953. - relative time (start, end, unit), start and end are of type
  954. integer, unit is of type string.
  955. - absolute time (start, end), start and end are of type datetime
  956. The end time may be None in case of a time instance.
  957. :param name: The name of the map
  958. :param mapset: The mapset of the map
  959. :param layer: The layer of the vector map
  960. :returns: The return value ofG_read_vector_timestamp and the timestamps
  961. """
  962. self._check_restart_server()
  963. self.client_conn.send([RPCDefs.READ_TIMESTAMP, RPCDefs.TYPE_VECTOR,
  964. name, mapset, layer])
  965. return self.client_conn.recv()
  966. def write_vector_timestamp(self, name, mapset, timestring, layer=None):
  967. """Write a file based vector timestamp
  968. Please have a look at the documentation G_write_vector_timestamp
  969. for the return values description.
  970. Note:
  971. Only timestamps pf maps from the current mapset can written.
  972. :param name: The name of the map
  973. :param mapset: The mapset of the map
  974. :param timestring: A GRASS datetime C-library compatible string
  975. :param layer: The layer of the vector map
  976. :returns: The return value of G_write_vector_timestamp
  977. """
  978. self._check_restart_server()
  979. self.client_conn.send([RPCDefs.WRITE_TIMESTAMP, RPCDefs.TYPE_VECTOR,
  980. name, mapset, layer, timestring])
  981. return self.client_conn.recv()
  982. def available_mapsets(self):
  983. """Return all available mapsets the user can access as a list of strings
  984. :returns: Names of available mapsets as list of strings
  985. """
  986. self._check_restart_server()
  987. self.client_conn.send([RPCDefs.AVAILABLE_MAPSETS, ])
  988. return self.client_conn.recv()
  989. def get_driver_name(self, mapset=None):
  990. """Return the temporal database driver of a specific mapset
  991. :param mapset: Name of the mapset
  992. :returns: Name of the driver or None if no temporal database present
  993. """
  994. self._check_restart_server()
  995. self.client_conn.send([RPCDefs.GET_DRIVER_NAME, mapset])
  996. return self.client_conn.recv()
  997. def get_database_name(self, mapset=None):
  998. """Return the temporal database name of a specific mapset
  999. :param mapset: Name of the mapset
  1000. :returns: Name of the database or None if no temporal database present
  1001. """
  1002. self._check_restart_server()
  1003. self.client_conn.send([RPCDefs.GET_DATABASE_NAME, mapset])
  1004. return self.client_conn.recv()
  1005. def get_mapset(self):
  1006. """Return the current mapset
  1007. :returns: Name of the current mapset
  1008. """
  1009. self._check_restart_server()
  1010. self.client_conn.send([RPCDefs.G_MAPSET, ])
  1011. return self.client_conn.recv()
  1012. def get_location(self):
  1013. """Return the location
  1014. :returns: Name of the location
  1015. """
  1016. self._check_restart_server()
  1017. self.client_conn.send([RPCDefs.G_LOCATION, ])
  1018. return self.client_conn.recv()
  1019. def get_gisdbase(self):
  1020. """Return the gisdatabase
  1021. :returns: Name of the gisdatabase
  1022. """
  1023. self._check_restart_server()
  1024. self.client_conn.send([RPCDefs.G_GISDBASE, ])
  1025. return self.client_conn.recv()
  1026. def fatal_error(self, mapset=None):
  1027. """Return the temporal database name of a specific mapset
  1028. :param mapset: Name of the mapset
  1029. :returns: Name of the database or None if no temporal database present
  1030. """
  1031. self._check_restart_server()
  1032. self.client_conn.send([RPCDefs.G_FATAL_ERROR])
  1033. def stop(self):
  1034. """Stop the messenger server and close the pipe
  1035. This method should be called at exit using the package atexit
  1036. """
  1037. if self.server is not None and self.server.is_alive():
  1038. self.client_conn.send([0, ])
  1039. self.server.join(5)
  1040. self.server.terminate()
  1041. if self.client_conn is not None:
  1042. self.client_conn.close()
  1043. if __name__ == "__main__":
  1044. import doctest
  1045. doctest.testmod()