code: def lock(self):
          
          provider = self.get_provider()
          reporter = self.get_reporter()
          resolver = resolvelib.Resolver(provider, reporter)
  
          with vistir.cd(self.project.root):
              state = resolver.resolve(self.requirements)
  
          traces = trace_graph(state.graph)
  
          hash_cache = HashCache()
          for r in state.mapping.values():
              if not r.hashes:
                  r.hashes = get_hashes(hash_cache, r)
  
          set_metadata(
              state.mapping, traces,
              provider.fetched_dependencies,
              provider.collected_requires_pythons,
          )
  
          lockfile = plette.Lockfile.with_meta_from(self.project.pipfile)
          lockfile["default"] = _collect_derived_entries(
              state, traces, self.default_requirements,
          )
          lockfile["develop"] = _collect_derived_entries(
              state, traces, self.develop_requirements,
          )
          self.project.lockfile = lockfile
docs: Lock specified (abstract) requirements into (concrete) candidates.
  
          The locking procedure consists of four stages:
  
          * Resolve versions and dependency graph (powered by ResolveLib).
          * Walk the graph to determine "why" each candidate came to be, i.e.
            what top-level requirements result in a given candidate.
          * Populate hashes for resolved candidates.
          * Populate markers based on dependency specifications of each
            candidate, and the dependency graph.