{"id":900,"date":"2011-08-16T17:16:11","date_gmt":"2011-08-16T16:16:11","guid":{"rendered":"http:\/\/cyberelk.net\/tim\/?p=900"},"modified":"2014-05-20T09:06:42","modified_gmt":"2014-05-20T08:06:42","slug":"d-bus-and-python-asynchronous-method-implementation","status":"publish","type":"post","link":"https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/","title":{"rendered":"D-Bus and Python: asynchronous method implementation"},"content":{"rendered":"<p>This is a quick demonstration of how to implement a D-Bus method in Python using asynchronous callbacks.<\/p>\n<p>I recently <a href=\"https:\/\/cyberelk.net\/tim\/2011\/07\/22\/more-d-bus-goodness-in-system-config-printer\/\">added support<\/a> in system-config-printer for determining the best driver to use for a particular printer.\u00a0 This is an expensive operation, largely because of the time it takes to get a list of available drivers from CUPS, and the Python program providing the D-Bus service also provides other services.\u00a0 I wanted the program to be able to deal with other callers while the CUPS operation was in progress.\u00a0 Here&#8217;s how that was done.<\/p>\n<p><!--more--><\/p>\n<p>Firstly, I already had a class for asynchronous communication with CUPS.\u00a0 When using PolicyKit to talk to CUPS, the calls use D-Bus which provides an asynchronous API for method calls anyway.\u00a0 For calls not provided that way the fallback is to queue requests for a worker thread to deal with.<\/p>\n<p>Asynchronous D-Bus calls are made in Python by supplying &#8220;reply_handler&#8221; and &#8220;error_handler&#8221; named options in the method call, like this:<\/p>\n<pre>self._cupsconn.getPPDs2 (reply_handler=self._cups_getppds_reply,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 error_handler=self._cups_error)<\/pre>\n<p>That call returns right away, but the <tt>getPPDs2<\/tt> operation continues in the background.\u00a0 When it is done, my reply_handler function is called.\u00a0 Alternatively, if there is an error, the error_handler function is called.\u00a0 One or the other will be called, at which point the operation is finished with.<\/p>\n<p>So far so good from the client side, but the question is how to implement a D-Bus <em>service<\/em> in an asynchronous manner.\u00a0 Here is a reminder of what a synchronous D-Bus service implementation looks like.<\/p>\n<pre>#!\/usr\/bin\/python\r\nimport gobject\r\nimport dbus.service\r\nimport time\r\n\r\nBUS='com.example.Timer'\r\nPATH='\/com\/example\/Timer'\r\nIFACE='com.example.Timer'\r\nSTART_TIME=time.time ()\r\n\r\nclass Timer(dbus.service.Object):\r\n\u00a0\u00a0\u00a0 def __init__ (self):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.bus = dbus.SessionBus ()\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 bus_name = dbus.service.BusName (BUS, bus=self.bus)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 dbus.service.Object.__init__ (self, bus_name, PATH)\r\n\r\n\u00a0\u00a0\u00a0 @dbus.service.method(dbus_interface=IFACE,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 in_signature='i',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 out_signature='i')\r\n\u00a0\u00a0\u00a0 def Delay (self, seconds):\r\n        print \"Sleeping for %ds\" % seconds\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 time.sleep (seconds)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return seconds\r\n\r\ndef heartbeat():\r\n\u00a0\u00a0\u00a0 print \"Still alive at\", time.time () - START_TIME\r\n\u00a0\u00a0\u00a0 return True\r\n\r\nfrom dbus.glib import DBusGMainLoop\r\nDBusGMainLoop (set_as_default=True)\r\nloop = gobject.MainLoop ()\r\n# Start the heartbeat\r\nhandle = gobject.timeout_add_seconds (1, heartbeat)\r\n# Start the D-Bus service\r\ntimer = Timer ()\r\nloop.run ()<\/pre>\n<p>The Timer class is the D-Bus service.\u00a0 It has a single method, Delay, which delays for a number of seconds and returns that same number.\u00a0 The program sets a repeating 1-second timer to print &#8220;Still alive&#8221;, and the D-Bus calls into the Timer service are handled by the D-Bus main loop.<\/p>\n<p>How does this program behave?\u00a0 Here is its output.\u00a0 While it was running I used <a href=\"http:\/\/live.gnome.org\/DFeet\/\">D-Feet<\/a> to call Delay(3).<\/p>\n<pre>Still alive at 1.02512407303\r\nStill alive at 2.0262401104\r\nStill alive at 3.02633500099\r\nStill alive at 4.02575397491\r\nSleeping for 3s\r\nStill alive at 7.0756611824\r\nStill alive at 8.02573609352\r\nStill alive at 9.02583909035\r\nStill alive at 10.0259339809\r\n^CTraceback (most recent call last):\r\n\u00a0 File \"\/tmp\/demo.py\", line 36, in &lt;module&gt;\r\n\u00a0\u00a0\u00a0 loop.run ()\r\nKeyboardInterrupt<\/pre>\n<p>As you can see, while it was handling the call to the Delay method it could not do anything else.\u00a0 Here is a new version of the Delay function, this time implemented using asynchronous callbacks.<\/p>\n<pre>\u00a0\u00a0\u00a0 @dbus.service.method(dbus_interface=IFACE,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 in_signature='i',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 out_signature='i',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 async_callbacks=('reply_handler',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 'error_handler'))\r\n\u00a0\u00a0\u00a0 def Delay (self, seconds, reply_handler, error_handler):\r\n        print \"Sleeping for %ds\" % seconds\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 gobject.timeout_add_seconds (seconds,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 lambda: reply_handler (seconds))<\/pre>\n<p>You&#8217;ll notice that the D-Bus function decorator now contains an async_callbacks keyword.\u00a0 This keyword declares the method keywords that the function uses for reply and error handlers.\u00a0 Here, I&#8217;ve stuck with the usual &#8220;reply_handler&#8221; and &#8220;error_handler&#8221; names, and added those same names to the definition of the Delay function on the next line.<\/p>\n<p>This time, when the D-Bus main loop calls the Delay method it will also provide the reply and error callbacks.\u00a0 When the Delay method returns, its return value is ignored.\u00a0 The D-Bus call is only ended by calling one of the callbacks.\u00a0 In this very simple implementation, I&#8217;ve arranged for that to happen by setting a timeout.<\/p>\n<p>How does the program behave now?<\/p>\n<pre>Still alive at 1.07246303558\r\nStill alive at 2.07272696495\r\nStill alive at 3.07237696648\r\nStill alive at 4.07276511192\r\nSleeping for 3s\r\nStill alive at 5.07263112068\r\nStill alive at 6.07277011871\r\nStill alive at 7.07274699211\r\nStill alive at 8.07311701775\r\nStill alive at 9.07332015038\r\nStill alive at 10.0734181404\r\nStill alive at 11.0735199451\r\n^CTraceback (most recent call last):\r\n\u00a0 File \"\/tmp\/demo-async.py\", line 38, in &lt;module&gt;\r\n\u00a0\u00a0\u00a0 loop.run ()\r\nKeyboardInterrupt<\/pre>\n<p>Much better.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is a quick demonstration of how to implement a D-Bus method in Python using asynchronous callbacks. I recently added support in system-config-printer for determining the best driver to use for a particular printer.\u00a0 This is an expensive operation, largely because of the time it takes to get a list of available drivers from CUPS, [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[3],"tags":[60,35],"class_list":["post-900","post","type-post","status-publish","format-standard","hentry","category-software","tag-dbus","tag-python"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>D-Bus and Python: asynchronous method implementation - PRINT HEAD<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/\" \/>\n<meta property=\"og:locale\" content=\"en_GB\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"D-Bus and Python: asynchronous method implementation - PRINT HEAD\" \/>\n<meta property=\"og:description\" content=\"This is a quick demonstration of how to implement a D-Bus method in Python using asynchronous callbacks. I recently added support in system-config-printer for determining the best driver to use for a particular printer.\u00a0 This is an expensive operation, largely because of the time it takes to get a list of available drivers from CUPS, [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/\" \/>\n<meta property=\"og:site_name\" content=\"PRINT HEAD\" \/>\n<meta property=\"article:published_time\" content=\"2011-08-16T16:16:11+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2014-05-20T08:06:42+00:00\" \/>\n<meta name=\"author\" content=\"Tim Waugh\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Tim Waugh\" \/>\n\t<meta name=\"twitter:label2\" content=\"Estimated reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/2011\\\/08\\\/16\\\/d-bus-and-python-asynchronous-method-implementation\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/2011\\\/08\\\/16\\\/d-bus-and-python-asynchronous-method-implementation\\\/\"},\"author\":{\"name\":\"Tim Waugh\",\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/#\\\/schema\\\/person\\\/23b749f30a67f1b1c6af17024fc94bf6\"},\"headline\":\"D-Bus and Python: asynchronous method implementation\",\"datePublished\":\"2011-08-16T16:16:11+00:00\",\"dateModified\":\"2014-05-20T08:06:42+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/2011\\\/08\\\/16\\\/d-bus-and-python-asynchronous-method-implementation\\\/\"},\"wordCount\":480,\"publisher\":{\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/#\\\/schema\\\/person\\\/23b749f30a67f1b1c6af17024fc94bf6\"},\"keywords\":[\"dbus\",\"python\"],\"articleSection\":[\"Software\"],\"inLanguage\":\"en-GB\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/2011\\\/08\\\/16\\\/d-bus-and-python-asynchronous-method-implementation\\\/\",\"url\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/2011\\\/08\\\/16\\\/d-bus-and-python-asynchronous-method-implementation\\\/\",\"name\":\"D-Bus and Python: asynchronous method implementation - PRINT HEAD\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/#website\"},\"datePublished\":\"2011-08-16T16:16:11+00:00\",\"dateModified\":\"2014-05-20T08:06:42+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/2011\\\/08\\\/16\\\/d-bus-and-python-asynchronous-method-implementation\\\/#breadcrumb\"},\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/cyberelk.net\\\/tim\\\/2011\\\/08\\\/16\\\/d-bus-and-python-asynchronous-method-implementation\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/2011\\\/08\\\/16\\\/d-bus-and-python-asynchronous-method-implementation\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"D-Bus and Python: asynchronous method implementation\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/#website\",\"url\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/\",\"name\":\"PRINT HEAD\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/#\\\/schema\\\/person\\\/23b749f30a67f1b1c6af17024fc94bf6\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-GB\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/#\\\/schema\\\/person\\\/23b749f30a67f1b1c6af17024fc94bf6\",\"name\":\"Tim Waugh\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-GB\",\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/wp-content\\\/uploads\\\/2023\\\/01\\\/printhead.png\",\"url\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/wp-content\\\/uploads\\\/2023\\\/01\\\/printhead.png\",\"contentUrl\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/wp-content\\\/uploads\\\/2023\\\/01\\\/printhead.png\",\"width\":731,\"height\":140,\"caption\":\"Tim Waugh\"},\"logo\":{\"@id\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/wp-content\\\/uploads\\\/2023\\\/01\\\/printhead.png\"},\"sameAs\":[\"http:\\\/\\\/cyberelk.net\\\/tim\"],\"url\":\"https:\\\/\\\/cyberelk.net\\\/tim\\\/author\\\/twaugh\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"D-Bus and Python: asynchronous method implementation - PRINT HEAD","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/","og_locale":"en_GB","og_type":"article","og_title":"D-Bus and Python: asynchronous method implementation - PRINT HEAD","og_description":"This is a quick demonstration of how to implement a D-Bus method in Python using asynchronous callbacks. I recently added support in system-config-printer for determining the best driver to use for a particular printer.\u00a0 This is an expensive operation, largely because of the time it takes to get a list of available drivers from CUPS, [&hellip;]","og_url":"https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/","og_site_name":"PRINT HEAD","article_published_time":"2011-08-16T16:16:11+00:00","article_modified_time":"2014-05-20T08:06:42+00:00","author":"Tim Waugh","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Tim Waugh","Estimated reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/#article","isPartOf":{"@id":"https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/"},"author":{"name":"Tim Waugh","@id":"https:\/\/cyberelk.net\/tim\/#\/schema\/person\/23b749f30a67f1b1c6af17024fc94bf6"},"headline":"D-Bus and Python: asynchronous method implementation","datePublished":"2011-08-16T16:16:11+00:00","dateModified":"2014-05-20T08:06:42+00:00","mainEntityOfPage":{"@id":"https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/"},"wordCount":480,"publisher":{"@id":"https:\/\/cyberelk.net\/tim\/#\/schema\/person\/23b749f30a67f1b1c6af17024fc94bf6"},"keywords":["dbus","python"],"articleSection":["Software"],"inLanguage":"en-GB"},{"@type":"WebPage","@id":"https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/","url":"https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/","name":"D-Bus and Python: asynchronous method implementation - PRINT HEAD","isPartOf":{"@id":"https:\/\/cyberelk.net\/tim\/#website"},"datePublished":"2011-08-16T16:16:11+00:00","dateModified":"2014-05-20T08:06:42+00:00","breadcrumb":{"@id":"https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/#breadcrumb"},"inLanguage":"en-GB","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/cyberelk.net\/tim\/2011\/08\/16\/d-bus-and-python-asynchronous-method-implementation\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cyberelk.net\/tim\/"},{"@type":"ListItem","position":2,"name":"D-Bus and Python: asynchronous method implementation"}]},{"@type":"WebSite","@id":"https:\/\/cyberelk.net\/tim\/#website","url":"https:\/\/cyberelk.net\/tim\/","name":"PRINT HEAD","description":"","publisher":{"@id":"https:\/\/cyberelk.net\/tim\/#\/schema\/person\/23b749f30a67f1b1c6af17024fc94bf6"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cyberelk.net\/tim\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-GB"},{"@type":["Person","Organization"],"@id":"https:\/\/cyberelk.net\/tim\/#\/schema\/person\/23b749f30a67f1b1c6af17024fc94bf6","name":"Tim Waugh","image":{"@type":"ImageObject","inLanguage":"en-GB","@id":"https:\/\/cyberelk.net\/tim\/wp-content\/uploads\/2023\/01\/printhead.png","url":"https:\/\/cyberelk.net\/tim\/wp-content\/uploads\/2023\/01\/printhead.png","contentUrl":"https:\/\/cyberelk.net\/tim\/wp-content\/uploads\/2023\/01\/printhead.png","width":731,"height":140,"caption":"Tim Waugh"},"logo":{"@id":"https:\/\/cyberelk.net\/tim\/wp-content\/uploads\/2023\/01\/printhead.png"},"sameAs":["http:\/\/cyberelk.net\/tim"],"url":"https:\/\/cyberelk.net\/tim\/author\/twaugh\/"}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pnnS2-ew","_links":{"self":[{"href":"https:\/\/cyberelk.net\/tim\/wp-json\/wp\/v2\/posts\/900","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cyberelk.net\/tim\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cyberelk.net\/tim\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cyberelk.net\/tim\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/cyberelk.net\/tim\/wp-json\/wp\/v2\/comments?post=900"}],"version-history":[{"count":12,"href":"https:\/\/cyberelk.net\/tim\/wp-json\/wp\/v2\/posts\/900\/revisions"}],"predecessor-version":[{"id":928,"href":"https:\/\/cyberelk.net\/tim\/wp-json\/wp\/v2\/posts\/900\/revisions\/928"}],"wp:attachment":[{"href":"https:\/\/cyberelk.net\/tim\/wp-json\/wp\/v2\/media?parent=900"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cyberelk.net\/tim\/wp-json\/wp\/v2\/categories?post=900"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cyberelk.net\/tim\/wp-json\/wp\/v2\/tags?post=900"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}