docs: def fetch_job(config):
      '''
      Fetch any available work from the OpenSubmit server and
      return an according job object.
  
      Returns None if no work is available.
  
      Errors are reported by this function directly.
      '''
      url = "%s/jobs/?Secret=%s&UUID=%s" % (config.get("Server", "url"),
                                            config.get("Server", "secret"),
                                            config.get("Server", "uuid"))
  
      try:
          # Fetch information from server
          result = urlopen(url)
          headers = result.info()
          logger.debug("Raw job data: " + str(result.headers).replace('\n', ', '))
          if not compatible_api_version(headers["APIVersion"]):
              # No proper reporting possible, so only logging.
              logger.error("Incompatible API version. Please update OpenSubmit.")
              return None
  
          if headers["Action"] == "get_config":
              # The server does not know us,
              # so it demands registration before hand.
              logger.info("Machine unknown on server, sending registration ...")
              send_hostinfo(config)
              return None
  
          # Create job object with information we got
          from .job import Job
          job = Job(config)
  
          job.submitter_name = headers['SubmitterName']
          job.author_names = headers['AuthorNames']
          job.submitter_studyprogram = headers['SubmitterStudyProgram']
          job.course = headers['Course']
          job.assignment = headers['Assignment']
          job.action = headers["Action"]
          job.file_id = headers["SubmissionFileId"]
          job.sub_id = headers["SubmissionId"]
          job.file_name = headers["SubmissionOriginalFilename"]
          job.submitter_student_id = headers["SubmitterStudentId"]
          if "Timeout" in headers:
              job.timeout = int(headers["Timeout"])
          if "PostRunValidation" in headers:
              # Ignore server-given host + port and use the configured one instead
              # This fixes problems with the arbitrary Django LiveServer port choice
              # It would be better to return relative URLs only for this property,
              # but this is a Bernhard-incompatible API change
              from urllib.parse import urlparse
              relative_path = urlparse(headers["PostRunValidation"]).path
              job.validator_url = config.get("Server", "url") + relative_path
          job.working_dir = create_working_dir(config, job.sub_id)
  
          # Store submission in working directory
          submission_fname = job.working_dir + job.file_name
          with open(submission_fname, 'wb') as target:
              target.write(result.read())
          assert(os.path.exists(submission_fname))
  
          # Store validator package in working directory
          validator_fname = job.working_dir + 'download.validator'
          fetch(job.validator_url, validator_fname)
  
          try:
              prepare_working_directory(job, submission_fname, validator_fname)
          except JobException as e:
              job.send_fail_result(e.info_student, e.info_tutor)
              return None
          logger.debug("Got job: " + str(job))
          return job
      except HTTPError as e:
          if e.code == 404:
              logger.debug("Nothing to do.")
              return None
      except URLError as e:
          logger.error("Error while contacting {0}: {1}".format(url, str(e)))
          return None