I need to reach manifests of a lot of images on Docker hub, but every GET request for getting manifest is counted as a pull, as a result, I am restricted by the rate limits of docker hub. Is there a way to get the manifest with a HEAD request instead of GET from the API?
Edit: From Docker registry API documentation :
GET /v2//manifests/: Fetch the manifest identified by name and reference where reference can be a tag or digest. A HEAD request can also be issued to this endpoint to obtain resource information without receiving all data.
So I assume we can get related information with an HEAD request.
A shell script to do that looks like:
#!/bin/sh
ref="${1:-library/ubuntu:latest}"
sha="${ref#*#}"
if [ "$sha" = "$ref" ]; then
sha=""
fi
wosha="${ref%%#*}"
repo="${wosha%:*}"
tag="${wosha##*:}"
if [ "$tag" = "$wosha" ]; then
tag="latest"
fi
apio="application/vnd.oci.image.index.v1+json"
apiol="application/vnd.oci.image.manifest.v1+json"
apid="application/vnd.docker.distribution.manifest.v2+json"
apidl="application/vnd.docker.distribution.manifest.list.v2+json"
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
| jq -r '.token')
curl -H "Accept: ${apio}" -H "Accept: ${apiol}" -H "Accept: ${apid}" -H "Accept: ${apidl}" \
-H "Authorization: Bearer $token" \
-I -s "https://registry-1.docker.io/v2/${repo}/manifests/${sha:-$tag}"
Note that the head request only shows the headers, and the most useful one is docker-content-digest so you can get the digest for a specific tag:
HTTP/1.1 200 OK
content-length: 1416
content-type: application/vnd.docker.distribution.manifest.list.v2+json
docker-content-digest: sha256:20fa2d7bb4de7723f542be5923b06c4d704370f0390e4ae9e1c833c8785644c1
docker-distribution-api-version: registry/2.0
etag: "sha256:20fa2d7bb4de7723f542be5923b06c4d704370f0390e4ae9e1c833c8785644c1"
date: Thu, 08 Sep 2022 17:45:42 GMT
strict-transport-security: max-age=31536000
ratelimit-limit: 100;w=21600
ratelimit-remaining: 100;w=21600
docker-ratelimit-source: 68.100.24.47
If you want to do this for other registries, you'll need to adjust the authentication. Both go-containerregistry's crane and regclient's regctl tools have image digest commands that handle the authentication and return just the digest.
I have two Docker containers. Container A is connected to a postgresql database and Container B is a client which should put data into the database with "curl" through port 9292 of A. This is the command which I'm running with subprocess() from a python script in B:
broker_addr = "pactbroker"
broker_port = "9292"
command = "curl -v -XPUT -H 'Content-Type: application/json' -u 'pact_broker:pact_broker' -d#repo/consumer-provider.json http://" + broker_addr + ":" + broker_port + "/pacts/provider/Provider/consumer/Consumer/version/1.0.0"
subprocess.run('exec bash -c "' + command + '"', shell=True)
I've set up both containers locally and this worked perfectly. However when I'm trying to do the same in an Azure Devops Pipeline I get the following error message:
curl: (7) Failed to connect to pactbroker port 9292: Connection refused
I tried to use the --network flag for both containers but this didn't change anything. These are the commands with which I tried to build Container A. The name should be correct, it runs on TCP port 9292 and I also exposed the port:
run --network pactnet --name pactbroker -p 9292:9292 --expose 9292 --expose 1234 --expose 5432 --env PGPASSWORD="password" -e PACT_BROKER_DATABASE_USERNAME=pact_broker -e PACT_BROKER_DATABASE_PASSWORD=pact_broker -e PACT_BROKER_DATABASE_HOST=hostname -e PACT_BROKER_DATABASE_NAME=pact_broker -d img_pactbroker
Here is the same command as docker-compose file:
version: "3"
services:
pactbroker:
image: img_pactbroker
container_name: pactbroker
expose:
- 9292
ports:
- 9292:9292
environment:
PACT_BROKER_DATABASE_HOST: "hostname"
PACT_BROKER_DATABASE_NAME: "pact_broker"
PACT_BROKER_DATABASE_USERNAME: "pact_broker"
PACT_BROKER_DATABASE_PASSWORD: "pact_broker"
PGPASSWORD: "password"
networks:
default:
external: true
name: pactnet
volumes:
postgres-volume:
My first guess was that it might have something to do with the proxy so I added the proxy flag to my python command:
command = "curl --proxy http://proxyaddress:8080 -v -XPUT -H 'Content-Type: application/json' -u 'pact_broker:pact_broker' -d#repo/consumer-provider.json http://" + broker_addr + ":" + broker_port + "/pacts/provider/Provider/consumer/Consumer/version/1.0.0"
This resulted in a Bad Gateway Error. From my understanding, the command tried to push the data to the proxy now:
> Authorization: Basic cGFjdF9icm9rZXI6cGFjdF9icm9rZXI=
> User-Agent: curl/7.77.0
> Accept: */*
> Proxy-Connection: Keep-Alive
> Content-Type: application/json
> Content-Length: 2513
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 502 Bad Gateway
< Server: squid/3.5.27
< Mime-Version: 1.0
< Date: Tue, 13 Jul 2021 13:25:52 GMT
< Content-Type: text/html;charset=utf-8
< Content-Length: 3835
< X-Squid-Error: ERR_ZERO_SIZE_OBJECT 0
< Vary: Accept-Language
< Content-Language: en
< X-Cache: MISS from proxyserver02
< X-Cache-Lookup: MISS from proxyserver02:8080
< Via: 1.1 proxyserver02 (squid/3.5.27)
< Connection: keep-alive
I also tried to reset the proxy environment when building the container but this didn't help either.
--env http_proxy="" --env https_proxy=""
This is the Dockerfile with which I build the image for Container A:
FROM pactfoundation/pact-broker:latest
ENV VSTSUSER 1003
ENV GEM_HOME="/usr/local/bundle"
ENV PATH $GEM_HOME/bin:$GEM_HOME/gems/bin:$PATH
WORKDIR $HOME
USER root
RUN apk update && apk add
RUN apk upgrade
RUN apk update && apk add --no-cache --virtual build-dependencies build-base
RUN apk update && apk add bash
RUN apk add postgresql
RUN apk add ruby
I don't know what could cause this connection problem. I must be missing something.
Thank you for any help!
I highly recommend using docker-composer, reading individual lines is painful to look at.
Two, using "network" does not make machine (machine = container) A know that machine B is picking up requests with the name "app" (or any other name).
I leave an example of how to ask the question with docker compose. It is also likely that you are missing the "extra_hosts" tag.
I am sorry to leave this as an answer, but my reputation does not
allow me to comment.
I'm trying to make a docker container of my rust programme, let's look
Dockerfile
FROM debian
RUN apt-get update && \
apt-get -y upgrade && \
apt-get -y install git curl g++ build-essential
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
WORKDIR /usr/src/app
RUN git clone https://github.com/unegare/rust-actix-rest.git
RUN ["/bin/bash", "-c", "source $HOME/.cargo/env; cd ./rust-actix-rest/; cargo build --release; mkdir uploaded"]
EXPOSE 8080
ENTRYPOINT ["/bin/bash", "-c", "echo 'Hello there!'; source $HOME/.cargo/env; cd ./rust-actix-rest/; cargo run --release"]
cmd to run: docker run -it -p 8080:8080 rust_rest_api/dev
but curl from outside curl -i -X POST -F files[]=#img.png 127.0.0.1:8080/upload results into curl: (56) Recv failure: Соединение разорвано другой стороной i.e. refused by the other side of the channel
but inside the container:
root#43598d5d9e85:/usr/src/app# lsof -i
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
actix_003 6 root 3u IPv4 319026 0t0 TCP localhost:http-alt (LISTEN)
but running the programme without docker works properly and processes the same request from curl adequately.
and inside the container:
root#43598d5d9e85:/usr/src/app# curl -i -X POST -F files[]=#i.jpg 127.0.0.1:8080/upload
HTTP/1.1 100 Continue
HTTP/1.1 201 Created
content-length: 70
content-type: application/json
date: Wed, 24 Jul 2019 08:00:54 GMT
{"keys":["uploaded/5nU1nHznvKRGbkQaWAGJKpLSG4nSAYfzCdgMxcx4U2mF.jpg"]}
What is the problem from outside?
If you're like myself and followed the examples on the Actix website, you might have written something like this, or some variation thereof:
fn main() {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(index))
.route("/again", web::get().to(index2))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
The issue here is that you're binding to a specific IP, rather than using 0.0.0.0 to bind to all IPs on the host container. I had the same issue as you and solved it by changing my code to:
fn main() {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(index))
.route("/again", web::get().to(index2))
})
.bind("0.0.0.0:8088")
.unwrap()
.run()
.unwrap();
}
This might not be the issue for you, I couldn't know without seeing the code to run the server.
To complete what John said, in my case I had to use a tuple: .bind( ("0.0.0.0", 8088) )
Similar to the question "What´s the sha256 code of a docker image?", I would like to find the digest of a Docker image. I can see the digest when I download an image:
$ docker pull waisbrot/wait:latest
latest: Pulling from waisbrot/wait
Digest: sha256:6f2185daa4ab1711181c30d03f565508e8e978ebd0f263030e7de98deee5f330
Status: Image is up to date for waisbrot/wait:latest
$
Another question, What is the Docker registry v2 API endpoint to get the digest for an image has an answer suggesting the Docker-Content-Digest header.
I can see that there is a Docker-Content-Digest header when I fetch the manifest for the image:
$ curl 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:waisbrot/wait:pull' -H "Authorization: Basic ${username_password_base64}"
# store the resulting token in DT
$ curl -v https://registry-1.docker.io/v2/waisbrot/wait/manifests/latest -H "Authorization: Bearer $DT" -XHEAD
* Trying 52.7.141.30...
* Connected to registry-1.docker.io (52.7.141.30) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.docker.io
* Server certificate: RapidSSL SHA256 CA - G3
* Server certificate: GeoTrust Global CA
> GET /v2/waisbrot/wait/manifests/latest HTTP/1.1
> Host: registry-1.docker.io
> User-Agent: curl/7.43.0
> Accept: */*
> Authorization: Bearer LtVRw-etc-etc-etc
>
< HTTP/1.1 200 OK
< Content-Length: 4974
< Content-Type: application/vnd.docker.distribution.manifest.v1+prettyjws
< Docker-Content-Digest: sha256:128c6e3534b842a2eec139999b8ce8aa9a2af9907e2b9269550809d18cd832a3
< Docker-Distribution-Api-Version: registry/2.0
< Etag: "sha256:128c6e3534b842a2eec139999b8ce8aa9a2af9907e2b9269550809d18cd832a3"
< Date: Wed, 07 Sep 2016 16:37:15 GMT
< Strict-Transport-Security: max-age=31536000
However, this header isn't the same. The pull command got me 6f21 and the header shows 128c. Further, the pull command doesn't work for that digest:
$ docker pull waisbrot/wait#sha256:128c6e3534b842a2eec139999b8ce8aa9a2af9907e2b9269550809d18cd832a3
Error response from daemon: manifest unknown: manifest unknown
whereas things work as I want when I have the correct digest:
$ docker pull waisbrot/wait#sha256:6f2185daa4ab1711181c30d03f565508e8e978ebd0f263030e7de98deee5f330 12:46 waisbrot#influenza
sha256:6f2185daa4ab1711181c30d03f565508e8e978ebd0f263030e7de98deee5f330: Pulling from waisbrot/wait
Digest: sha256:6f2185daa4ab1711181c30d03f565508e8e978ebd0f263030e7de98deee5f330
Status: Image is up to date for waisbrot/wait#sha256:6f2185daa4ab1711181c30d03f565508e8e978ebd0f263030e7de98deee5f330
What I'm looking for is a way to translate the latest tag (which changes all the time) into a fixed digest that I can reliably pull. But I don't want to actually pull it down in order to do this translation.
edit 2022-10-04:
# INPUT
REPO=waisbrot/wait
user=my-user
password=my-password
# Get TOKEN
username_password_base64=$(echo -n $user:$password | base64)
TOKEN=$(curl -s -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Basic ${username_password_base64}" \
'https://auth.docker.io/token?service=registry.docker.io&scope=repository:waisbrot/wait:pull' \
| jq -r .token)
# GET Digest from v2 API
curl -s -D - -H "Authorization: Bearer $TOKEN" \
https://registry-1.docker.io/v2/waisbrot/wait/manifests/latest 2>&1 \
| grep docker-content-digest \
| cut -d' ' -f2
original answer:
For newer versions of Docker, the inspect command provides the correct value (requires the image to have been pulled as Jan Hudec has pointed out in the comments):
docker inspect --format='{{index .RepoDigests 0}}' waisbrot/wait
For older versions, fetch the value from the repository following this example with the main Docker repo:
curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Basic ${username_password_base64}" \
'https://auth.docker.io/token?service=registry.docker.io&scope=repository:waisbrot/wait:pull'
Naive attempts to fetch that value fail because the default content-type being selected by the server is application/vnd.docker.distribution.manifest.v1+prettyjws (a v1 manifest) and you need to v2 manifest. Therefore, you need to set the Accept header to application/vnd.docker.distribution.manifest.v2+json.
This is how you do it today using a V2 manifest.
docker manifest inspect <REMOTE IMAGE>:<TAG> -v
Your output is JSON:
{
...
"Descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:d13e102941a9f7bd417440f62f9cb29de35f6acb13a26cbf6a34f4c7340f0b63",
"size": 3255,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
...
}
With 2 http requests, you can get it. The first one to get an authentication token, and the second to get the image digest list by architecture and variant:
token=$(curl --silent "https://auth.docker.io/token?scope=repository:$image:pull&service=registry.docker.io" | jq -r '.token')
curl -s --header "Accept: application/vnd.docker.distribution.manifest.list.v2+json" --header "Authorization: Bearer ${token}" "https://registry-1.docker.io/v2/$image/manifests/$tag" | jq -r '.manifests|.[]| "\(.digest) \(.platform.architecture) \(.platform.variant)"'
Example with:
image=library/nginx
tag=stable-alpine
sha256:8853c7e938c2aa5d9d7439e698f0e700f058df8414a83134a09fcbb68bb0707a amd64 null
sha256:dbcd23f95b94018fe72bfdb356e40f4ae8b95063883f3456fedaed1c02204ed4 arm v6
sha256:d3670edcd50bb07cae303767426adf9bc7ba0219736148d30e6f30dd4e08695c arm v7
sha256:0bcd76faa141e4fa37e875834b3994261e0cfc94b7233ac84896381315b845ca arm64 v8
sha256:da8e62ddb3fab89ff4fa0271dbe230f849ab53402a71338503952437dcda1026 386 null
sha256:269bf99e100294b6b75fbdecf7b4ddbef8b29ea0a953e2e904452a50dbc923ab ppc64le null
sha256:103da50956034c157abeffbc869e2e38a4fabbf913bed8ae6ae7c59e646b28a1 s390x null
I encountered a task recently that required viewing the sha256 digest without necessarily pulling the image. The tool skopeo makes the registry API calls so you don't need to pull the image.
For example,
$ skopeo inspect --creds "username:password" docker://waisbrot/wait:latest
You could then pipe this to jq if you want to get just the digest value.
$ skopeo inspect --creds "username:password" \
docker://waisbrot/wait:latest | jq -r '.Digest'
sha256:6f2185daa4ab1711181c30d03f565508e8e978ebd0f263030e7de98deee5f330
I realise this issue is answered however either I am missing something or the current version of AWS ECR registry service does not work as expected.
When trying to get the digest from AWS ECR using either HEAD and also trying to switch the content-type does not return a digest value that I can use to pull an image using the registry Api.
To get this digest you have to get the manifest for the tag you are interested in and calculate the sha256 of the response Json as is, including the formatting, without the signature section
I struggled with this also. Here is a C# (dotnet core 5.0) implementation if anyone is intersted:
/**
TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:waisbrot/wait:pull" | jq -r .token)
curl -s -D - -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.docker.distribution.manifest.v2+json" https://index.docker.io/v2/waisbrot/wait/manifests/latest
*/
private string GetRemoteImageDigest(string image, string tag) {
using HttpClient client = new ();
var url = string.Format($"https://auth.docker.io/token?service=registry.docker.io&scope=repository:{image}:pull");
//var response = client.Send(new HttpRequestMessage(HttpMethod.Get, url));
var result = client.GetStringAsync(url);
var drt = JsonSerializer.Deserialize<DockerRegistryToken>(result.Result);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", drt.Token);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.docker.distribution.manifest.v2+json"));
var response = client.GetAsync(string.Format($"https://index.docker.io/v2/{image}/manifests/{tag}"));
var headers = response.Result.Headers;
IEnumerable<string> values;
headers.TryGetValues("Docker-Content-Digest", out values);
return values.FirstOrDefault();
}
DockerRegistryToken is defined as:
public class DockerRegistryToken{
[JsonPropertyName("token")]
public string Token { get; set; }
/// always null
[JsonPropertyName("access_token")]
public string AccessToken {get; set; }
[JsonPropertyName("expires_in")]
public int ExpiresInSeconds { get; set; }
[JsonPropertyName("issued_at")]
public DateTime IssuedAt { get; set; }
}
As mentioned in other answers, this digest did not match because you attempted to curl without an Accept header, and so the registry triggered a fallback to an older v1 image manifest:
< Content-Type: application/vnd.docker.distribution.manifest.v1+prettyjws
< Docker-Content-Digest: sha256:128c6e3534b842a2eec139999b8ce8aa9a2af9907e2b9269550809d18cd832a3
You can query Hub with curl using a script like:
#!/bin/sh
ref="${1:-library/ubuntu:latest}"
sha="${ref#*#}"
if [ "$sha" = "$ref" ]; then
sha=""
fi
wosha="${ref%%#*}"
repo="${wosha%:*}"
tag="${wosha##*:}"
if [ "$tag" = "$wosha" ]; then
tag="latest"
fi
cto="application/vnd.oci.image.index.v1+json"
ctol="application/vnd.oci.image.manifest.v1+json"
ctd="application/vnd.docker.distribution.manifest.v2+json"
ctdl="application/vnd.docker.distribution.manifest.list.v2+json"
token=$(curl -sL "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
| jq -r '.token')
curl -H "Accept: ${cto}" -H "Accept: ${ctol}" -H "Accept: ${ctd}" -H "Accept: ${ctdl}" \
-H "Authorization: Bearer $token" \
-I -sL "https://registry-1.docker.io/v2/${repo}/manifests/${sha:-$tag}"
However, that's limited since it's specific to Hub, and you still need to parse the headers. From the skopeo output, it's still pulling the entire manifest rather than a HEAD request, which will count against Hub rate limits.
Instead my two preferred tools for this are go-containerregistry/crane and regclient/regctl (I'm the author of the latter). Each has a digest command which automatically handles auth to different registries, includes the needed Accept headers, and parses the output to just the digest which is useful for scripting:
$ regctl image digest busybox
sha256:3b3128d9df6bbbcc92e2358e596c9fbd722a437a62bafbc51607970e9e3b8869
$ crane digest busybox
sha256:3b3128d9df6bbbcc92e2358e596c9fbd722a437a62bafbc51607970e9e3b8869
To avoid installing other tools, docker now has docker buildx imagetools inspect, but similar to skopeo, this is pulling the entire manifest rather than a HEAD request:
$ docker buildx imagetools inspect busybox
Name: docker.io/library/busybox:latest
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest: sha256:3b3128d9df6bbbcc92e2358e596c9fbd722a437a62bafbc51607970e9e3b8869
...
$ docker buildx imagetools inspect busybox --format '{{json .}}' | jq -r .manifest.digest
sha256:3b3128d9df6bbbcc92e2358e596c9fbd722a437a62bafbc51607970e9e3b8869
example for reg which requires redirect (curl follow 302)
REGISTRY_ADDRESS='registry.access.redhat.com'
image='ubi8/openjdk-17-runtime'
curl --silent -L \
--header "Accept: application/vnd.docker.distribution.manifest.v2+json" \
"https://$REGISTRY_ADDRESS/v2/$image/manifests/latest" |
jq -r '.config.digest'
Following up on ByteFlinger's suggestion, which did not have an example, I tried this, and this is how to calculate it:
$ docker-ls tag -registry https://myregistry.net:5000
spicysomtam/zookeeper:latest
requesting manifest . done
repository: spicysomtam/zookeeper
tagName: latest
digest: sha256:bd5dd80253171e4dffccbea7c639c90a63d5424aa2d7fe655aea766405c83036
$ curl -ns -H "Accept:
application/vnd.docker.distribution.manifest.v2+json" -X GET
https://myregistry.net:5000/v2/spicysomtam/zookeeper/manifests/latest|sha256sum
bd5dd80253171e4dffccbea7c639c90a63d5424aa2d7fe655aea766405c83036 -
$ docker images --digests |grep zookeeper
myregistry.net:5000/spicysomtam/zookeeper latest sha256:bd5dd80253171e4dffccbea7c639c90a63d5424aa2d7fe655aea766405c83036 a983e71ca22d 29 hours ago 584MB
You can get this using docker inspect:
docker inspect --format='{{index .RepoDigests 0}}' ${IMAGE_NAME}
Docs: https://docs.docker.com/engine/reference/commandline/inspect/
This has been in place since at least v1.9.
I'm trying to remove images from Docker Registry using API v2 folloving сommand:
curl -k -v -u 'docker:sdf' -X DELETE https://localhost:5000/v2/bkf/ebbg/manifests/1
But I get next error:
> DELETE /v2/bkf/ebbg/manifests/1 HTTP/1.1
> Authorization: Basic ZG9ja2VyOkRrZmxidmJoMjAx==
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
>
< HTTP/1.1 400 Bad Request
< Content-Type: application/json; charset=utf-8
< Docker-Distribution-Api-Version: registry/2.0
< X-Content-Type-Options: nosniff
< Date: Mon, 14 Mar 2016 07:56:13 GMT
< Content-Length: 98
<
{"errors":[{"code":"DIGEST_INVALID","message":"provided digest did not match uploaded content"}]}
Command:
curl -u 'docker:sdf' -X GET https://localhost:5000/v2/_catalog
show
{"repositories":["bkf/ebbg"]}
comand
curl -k -u 'docker:sdf' -X GET https://localhost:5000/v2/bkf/ebbg/tags/list
show
{"name":"bkf/ebbg","tags":["32","1","latest","12","33","34"]}
In what may be a problem or where did I go wrong?
I managed to get this working with deleting a tag but the repository still remains.
The first command you need will get you the digest:
curl -k -v -u 'docker:sdf' -X HEAD -v https://localhost:5000/v2/bkf/ebbg/manifests/1
That will return the in the header the digest you need.
< Docker-Content-Digest: sha256:xxxxxxx
You will then need to make the DELETE call using the digest:
curl -k -v -u 'docker:sdf' -X DELETE -v --header "Accept: application/vnd.docker.distribution.manifest.v2+json" https://localhost:5000/v2/bkf/ebbg/manifests/sha256:xxxxxxx
If successful it will return:
202 Accepted
This does remove the tag but, like I said, the repository still remains. I will need to do some more work on this to figure out why.
The answer to your use case probably is delete_docker_registry_image.py
I tried it on my registry and obviously it did the trick :)
As reading the python source code isn't too complicated, you can get from there what is done when and how - or simply just use it :P
hope it helps ...