diff --git a/docker/api/image.py b/docker/api/image.py
index f891e210..8493b38d 100644
--- a/docker/api/image.py
+++ b/docker/api/image.py
@@ -158,8 +158,6 @@ class ImageApiMixin(object):
         if not tag:
             repository, tag = utils.parse_repository_tag(repository)
         registry, repo_name = auth.resolve_repository_name(repository)
-        if repo_name.count(":") == 1:
-            repository, tag = repository.rsplit(":", 1)
 
         params = {
             'tag': tag,
@@ -174,7 +172,8 @@ class ImageApiMixin(object):
                 log.debug('Looking for auth config')
                 if not self._auth_configs:
                     log.debug(
-                        "No auth config in memory - loading from filesystem")
+                        "No auth config in memory - loading from filesystem"
+                    )
                     self._auth_configs = auth.load_config()
                 authcfg = auth.resolve_authconfig(self._auth_configs, registry)
                 # Do not fail here if no authentication exists for this
diff --git a/docker/auth/auth.py b/docker/auth/auth.py
index 416dd7c4..f771dedd 100644
--- a/docker/auth/auth.py
+++ b/docker/auth/auth.py
@@ -16,11 +16,9 @@ import base64
 import json
 import logging
 import os
-import warnings
 
 import six
 
-from .. import constants
 from .. import errors
 
 INDEX_NAME = 'index.docker.io'
@@ -31,31 +29,29 @@ LEGACY_DOCKER_CONFIG_FILENAME = '.dockercfg'
 log = logging.getLogger(__name__)
 
 
-def resolve_repository_name(repo_name, insecure=False):
-    if insecure:
-        warnings.warn(
-            constants.INSECURE_REGISTRY_DEPRECATION_WARNING.format(
-                'resolve_repository_name()'
-            ), DeprecationWarning
-        )
-
+def resolve_repository_name(repo_name):
     if '://' in repo_name:
         raise errors.InvalidRepository(
-            'Repository name cannot contain a scheme ({0})'.format(repo_name))
-    parts = repo_name.split('/', 1)
-    if '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost':
-        # This is a docker index repo (ex: foo/bar or ubuntu)
-        return INDEX_NAME, repo_name
-    if len(parts) < 2:
-        raise errors.InvalidRepository(
-            'Invalid repository name ({0})'.format(repo_name))
+            'Repository name cannot contain a scheme ({0})'.format(repo_name)
+        )
 
-    if 'index.docker.io' in parts[0]:
+    index_name, remote_name = split_repo_name(repo_name)
+    if index_name[0] == '-' or index_name[-1] == '-':
         raise errors.InvalidRepository(
-            'Invalid repository name, try "{0}" instead'.format(parts[1])
+            'Invalid index name ({0}). Cannot begin or end with a'
+            ' hyphen.'.format(index_name)
         )
+    return index_name, remote_name
+
 
-    return parts[0], parts[1]
+def split_repo_name(repo_name):
+    parts = repo_name.split('/', 1)
+    if len(parts) == 1 or (
+        '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost'
+    ):
+        # This is a docker index repo (ex: username/foobar or ubuntu)
+        return INDEX_NAME, repo_name
+    return tuple(parts)
 
 
 def resolve_authconfig(authconfig, registry=None):
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index 366f8696..560ee8e2 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -283,16 +283,14 @@ def convert_volume_binds(binds):
     return result
 
 
-def parse_repository_tag(repo):
-    column_index = repo.rfind(':')
-    if column_index < 0:
-        return repo, None
-    tag = repo[column_index + 1:]
-    slash_index = tag.find('/')
-    if slash_index < 0:
-        return repo[:column_index], tag
-
-    return repo, None
+def parse_repository_tag(repo_name):
+    parts = repo_name.rsplit('@', 1)
+    if len(parts) == 2:
+        return tuple(parts)
+    parts = repo_name.rsplit(':', 1)
+    if len(parts) == 2 and '/' not in parts[1]:
+        return tuple(parts)
+    return repo_name, None
 
 
 # Based on utils.go:ParseHost http://tinyurl.com/nkahcfh

Can't pull images with . In name.
Docker images that have a `.` in their name cannot be pulled with docker-py. This is a result of:

https://github.com/docker/docker-py/blob/master/docker/auth/auth.py#L46
diff --git a/tests/unit/auth_test.py b/tests/unit/auth_test.py
index 67830381..8e0b1d43 100644
--- a/tests/unit/auth_test.py
+++ b/tests/unit/auth_test.py
@@ -9,6 +9,7 @@ import shutil
 import tempfile
 
 from docker import auth
+from docker import errors
 
 from .. import base
 
@@ -29,25 +30,31 @@ class RegressionTest(base.BaseTestCase):
         assert b'_' in encoded
 
 
-class ResolveAuthTest(base.BaseTestCase):
-    auth_config = {
-        'https://index.docker.io/v1/': {'auth': 'indexuser'},
-        'my.registry.net': {'auth': 'privateuser'},
-        'http://legacy.registry.url/v1/': {'auth': 'legacyauth'}
-    }
-
+class ResolveRepositoryNameTest(base.BaseTestCase):
     def test_resolve_repository_name_hub_library_image(self):
         self.assertEqual(
             auth.resolve_repository_name('image'),
             ('index.docker.io', 'image'),
         )
 
+    def test_resolve_repository_name_dotted_hub_library_image(self):
+        self.assertEqual(
+            auth.resolve_repository_name('image.valid'),
+            ('index.docker.io', 'image.valid')
+        )
+
     def test_resolve_repository_name_hub_image(self):
         self.assertEqual(
             auth.resolve_repository_name('username/image'),
             ('index.docker.io', 'username/image'),
         )
 
+    def test_explicit_hub_index_library_image(self):
+        self.assertEqual(
+            auth.resolve_repository_name('index.docker.io/image'),
+            ('index.docker.io', 'image')
+        )
+
     def test_resolve_repository_name_private_registry(self):
         self.assertEqual(
             auth.resolve_repository_name('my.registry.net/image'),
@@ -90,6 +97,20 @@ class ResolveAuthTest(base.BaseTestCase):
             ('localhost', 'username/image'),
         )
 
+    def test_invalid_index_name(self):
+        self.assertRaises(
+            errors.InvalidRepository,
+            lambda: auth.resolve_repository_name('-gecko.com/image')
+        )
+
+
+class ResolveAuthTest(base.BaseTestCase):
+    auth_config = {
+        'https://index.docker.io/v1/': {'auth': 'indexuser'},
+        'my.registry.net': {'auth': 'privateuser'},
+        'http://legacy.registry.url/v1/': {'auth': 'legacyauth'}
+    }
+
     def test_resolve_authconfig_hostname_only(self):
         self.assertEqual(
             auth.resolve_authconfig(self.auth_config, 'my.registry.net'),
diff --git a/tests/unit/utils_test.py b/tests/unit/utils_test.py
index 3c9f6e2f..57ad4435 100644
--- a/tests/unit/utils_test.py
+++ b/tests/unit/utils_test.py
@@ -352,23 +352,55 @@ class ParseHostTest(base.BaseTestCase):
             assert parse_host(val, 'win32') == tcp_port
 
 
+class ParseRepositoryTagTest(base.BaseTestCase):
+    sha = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
+
+    def test_index_image_no_tag(self):
+        self.assertEqual(
+            parse_repository_tag("root"), ("root", None)
+        )
+
+    def test_index_image_tag(self):
+        self.assertEqual(
+            parse_repository_tag("root:tag"), ("root", "tag")
+        )
+
+    def test_index_user_image_no_tag(self):
+        self.assertEqual(
+            parse_repository_tag("user/repo"), ("user/repo", None)
+        )
+
+    def test_index_user_image_tag(self):
+        self.assertEqual(
+            parse_repository_tag("user/repo:tag"), ("user/repo", "tag")
+        )
+
+    def test_private_reg_image_no_tag(self):
+        self.assertEqual(
+            parse_repository_tag("url:5000/repo"), ("url:5000/repo", None)
+        )
+
+    def test_private_reg_image_tag(self):
+        self.assertEqual(
+            parse_repository_tag("url:5000/repo:tag"), ("url:5000/repo", "tag")
+        )
+
+    def test_index_image_sha(self):
+        self.assertEqual(
+            parse_repository_tag("root@sha256:{0}".format(self.sha)),
+            ("root", "sha256:{0}".format(self.sha))
+        )
+
+    def test_private_reg_image_sha(self):
+        self.assertEqual(
+            parse_repository_tag("url:5000/repo@sha256:{0}".format(self.sha)),
+            ("url:5000/repo", "sha256:{0}".format(self.sha))
+        )
+
+
 class UtilsTest(base.BaseTestCase):
     longMessage = True
 
-    def test_parse_repository_tag(self):
-        self.assertEqual(parse_repository_tag("root"),
-                         ("root", None))
-        self.assertEqual(parse_repository_tag("root:tag"),
-                         ("root", "tag"))
-        self.assertEqual(parse_repository_tag("user/repo"),
-                         ("user/repo", None))
-        self.assertEqual(parse_repository_tag("user/repo:tag"),
-                         ("user/repo", "tag"))
-        self.assertEqual(parse_repository_tag("url:5000/repo"),
-                         ("url:5000/repo", None))
-        self.assertEqual(parse_repository_tag("url:5000/repo:tag"),
-                         ("url:5000/repo", "tag"))
-
     def test_parse_bytes(self):
         self.assertEqual(parse_bytes("512MB"), (536870912))
         self.assertEqual(parse_bytes("512M"), (536870912))

pip install -e .
parse_log_pytest
requirements.txt
pytest pytest-cov flake8
apt-get update apt-get install -y gcc
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
coverage==7.8.0
-e git+https://github.com/docker/docker-py.git@1ca2bc58f0cf2e2cdda2734395bd3e7ad9b178bf#egg=docker_py
exceptiongroup==1.2.2
flake8==7.2.0
iniconfig==2.1.0
mccabe==0.7.0
packaging==24.2
pluggy==1.5.0
pycodestyle==2.13.0
pyflakes==3.3.2
pytest==8.3.5
pytest-cov==6.0.0
requests==2.5.3
six==1.17.0
tomli==2.2.1
websocket_client==0.32.0

name: docker-py
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - flake8==7.2.0
      - iniconfig==2.1.0
      - mccabe==0.7.0
      - packaging==24.2
      - pluggy==1.5.0
      - pycodestyle==2.13.0
      - pyflakes==3.3.2
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - requests==2.5.3
      - six==1.17.0
      - tomli==2.2.1
      - websocket-client==0.32.0
prefix: /opt/conda/envs/docker-py

tests/unit/auth_test.py::ResolveRepositoryNameTest::test_explicit_hub_index_library_image tests/unit/auth_test.py::ResolveRepositoryNameTest::test_invalid_index_name tests/unit/auth_test.py::ResolveRepositoryNameTest::test_resolve_repository_name_dotted_hub_library_image tests/unit/utils_test.py::ParseRepositoryTagTest::test_index_image_sha tests/unit/utils_test.py::ParseRepositoryTagTest::test_private_reg_image_sha
tests/unit/auth_test.py::RegressionTest::test_803_urlsafe_encode tests/unit/auth_test.py::ResolveRepositoryNameTest::test_resolve_repository_name_hub_image tests/unit/auth_test.py::ResolveRepositoryNameTest::test_resolve_repository_name_hub_library_image tests/unit/auth_test.py::ResolveRepositoryNameTest::test_resolve_repository_name_localhost tests/unit/auth_test.py::ResolveRepositoryNameTest::test_resolve_repository_name_localhost_with_username tests/unit/auth_test.py::ResolveRepositoryNameTest::test_resolve_repository_name_no_dots_but_port tests/unit/auth_test.py::ResolveRepositoryNameTest::test_resolve_repository_name_no_dots_but_port_and_username tests/unit/auth_test.py::ResolveRepositoryNameTest::test_resolve_repository_name_private_registry tests/unit/auth_test.py::ResolveRepositoryNameTest::test_resolve_repository_name_private_registry_with_port tests/unit/auth_test.py::ResolveRepositoryNameTest::test_resolve_repository_name_private_registry_with_username tests/unit/auth_test.py::ResolveAuthTest::test_resolve_authconfig_default_explicit_none tests/unit/auth_test.py::ResolveAuthTest::test_resolve_authconfig_default_registry tests/unit/auth_test.py::ResolveAuthTest::test_resolve_authconfig_fully_explicit tests/unit/auth_test.py::ResolveAuthTest::test_resolve_authconfig_hostname_only tests/unit/auth_test.py::ResolveAuthTest::test_resolve_authconfig_legacy_config tests/unit/auth_test.py::ResolveAuthTest::test_resolve_authconfig_no_match tests/unit/auth_test.py::ResolveAuthTest::test_resolve_authconfig_no_path tests/unit/auth_test.py::ResolveAuthTest::test_resolve_authconfig_no_path_trailing_slash tests/unit/auth_test.py::ResolveAuthTest::test_resolve_authconfig_no_path_wrong_insecure_proto tests/unit/auth_test.py::ResolveAuthTest::test_resolve_authconfig_no_path_wrong_secure_proto tests/unit/auth_test.py::ResolveAuthTest::test_resolve_authconfig_no_protocol tests/unit/auth_test.py::ResolveAuthTest::test_resolve_authconfig_path_wrong_proto tests/unit/auth_test.py::ResolveAuthTest::test_resolve_registry_and_auth_hub_image tests/unit/auth_test.py::ResolveAuthTest::test_resolve_registry_and_auth_library_image tests/unit/auth_test.py::ResolveAuthTest::test_resolve_registry_and_auth_private_registry tests/unit/auth_test.py::ResolveAuthTest::test_resolve_registry_and_auth_unauthenticated_registry tests/unit/auth_test.py::LoadConfigTest::test_load_config tests/unit/auth_test.py::LoadConfigTest::test_load_config_custom_config_env tests/unit/auth_test.py::LoadConfigTest::test_load_config_custom_config_env_utf8 tests/unit/auth_test.py::LoadConfigTest::test_load_config_custom_config_env_with_auths tests/unit/auth_test.py::LoadConfigTest::test_load_config_no_file tests/unit/auth_test.py::LoadConfigTest::test_load_config_with_random_name tests/unit/utils_test.py::HostConfigTest::test_create_host_config_invalid_cpu_cfs_types tests/unit/utils_test.py::HostConfigTest::test_create_host_config_no_options tests/unit/utils_test.py::HostConfigTest::test_create_host_config_no_options_newer_api_version tests/unit/utils_test.py::HostConfigTest::test_create_host_config_with_cpu_period tests/unit/utils_test.py::HostConfigTest::test_create_host_config_with_cpu_quota tests/unit/utils_test.py::HostConfigTest::test_create_host_config_with_oom_kill_disable tests/unit/utils_test.py::UlimitTest::test_create_host_config_dict_ulimit tests/unit/utils_test.py::UlimitTest::test_create_host_config_dict_ulimit_capitals tests/unit/utils_test.py::UlimitTest::test_create_host_config_obj_ulimit tests/unit/utils_test.py::UlimitTest::test_ulimit_invalid_type tests/unit/utils_test.py::LogConfigTest::test_create_host_config_dict_logconfig tests/unit/utils_test.py::LogConfigTest::test_create_host_config_obj_logconfig tests/unit/utils_test.py::LogConfigTest::test_logconfig_invalid_config_type tests/unit/utils_test.py::KwargsFromEnvTest::test_kwargs_from_env_empty tests/unit/utils_test.py::KwargsFromEnvTest::test_kwargs_from_env_no_cert_path tests/unit/utils_test.py::KwargsFromEnvTest::test_kwargs_from_env_tls tests/unit/utils_test.py::ConverVolumeBindsTest::test_convert_volume_binds_compact tests/unit/utils_test.py::ConverVolumeBindsTest::test_convert_volume_binds_complete tests/unit/utils_test.py::ConverVolumeBindsTest::test_convert_volume_binds_empty tests/unit/utils_test.py::ConverVolumeBindsTest::test_convert_volume_binds_list tests/unit/utils_test.py::ConverVolumeBindsTest::test_convert_volume_binds_no_mode tests/unit/utils_test.py::ConverVolumeBindsTest::test_convert_volume_binds_unicode_bytes_input tests/unit/utils_test.py::ConverVolumeBindsTest::test_convert_volume_binds_unicode_unicode_input tests/unit/utils_test.py::ParseEnvFileTest::test_parse_env_file_commented_line tests/unit/utils_test.py::ParseEnvFileTest::test_parse_env_file_invalid_line tests/unit/utils_test.py::ParseEnvFileTest::test_parse_env_file_proper tests/unit/utils_test.py::ParseHostTest::test_parse_host tests/unit/utils_test.py::ParseHostTest::test_parse_host_empty_value tests/unit/utils_test.py::ParseRepositoryTagTest::test_index_image_no_tag tests/unit/utils_test.py::ParseRepositoryTagTest::test_index_image_tag tests/unit/utils_test.py::ParseRepositoryTagTest::test_index_user_image_no_tag tests/unit/utils_test.py::ParseRepositoryTagTest::test_index_user_image_tag tests/unit/utils_test.py::ParseRepositoryTagTest::test_private_reg_image_no_tag tests/unit/utils_test.py::ParseRepositoryTagTest::test_private_reg_image_tag tests/unit/utils_test.py::UtilsTest::test_convert_filters tests/unit/utils_test.py::UtilsTest::test_decode_json_header tests/unit/utils_test.py::UtilsTest::test_parse_bytes tests/unit/utils_test.py::SplitCommandTest::test_split_command_with_unicode tests/unit/utils_test.py::PortsTest::test_build_port_bindings_with_matching_internal_port_ranges tests/unit/utils_test.py::PortsTest::test_build_port_bindings_with_matching_internal_ports tests/unit/utils_test.py::PortsTest::test_build_port_bindings_with_nonmatching_internal_port_ranges tests/unit/utils_test.py::PortsTest::test_build_port_bindings_with_nonmatching_internal_ports tests/unit/utils_test.py::PortsTest::test_build_port_bindings_with_one_port tests/unit/utils_test.py::PortsTest::test_build_port_bindings_with_port_range tests/unit/utils_test.py::PortsTest::test_host_only_with_colon tests/unit/utils_test.py::PortsTest::test_non_matching_length_port_ranges tests/unit/utils_test.py::PortsTest::test_port_and_range_invalid tests/unit/utils_test.py::PortsTest::test_port_only_with_colon tests/unit/utils_test.py::PortsTest::test_split_port_invalid tests/unit/utils_test.py::PortsTest::test_split_port_no_host_port tests/unit/utils_test.py::PortsTest::test_split_port_range_no_host_port tests/unit/utils_test.py::PortsTest::test_split_port_range_with_host_ip_no_port tests/unit/utils_test.py::PortsTest::test_split_port_range_with_host_port tests/unit/utils_test.py::PortsTest::test_split_port_range_with_protocol tests/unit/utils_test.py::PortsTest::test_split_port_with_host_ip tests/unit/utils_test.py::PortsTest::test_split_port_with_host_ip_no_port tests/unit/utils_test.py::PortsTest::test_split_port_with_host_port tests/unit/utils_test.py::PortsTest::test_split_port_with_protocol tests/unit/utils_test.py::ExcludePathsTest::test_directory tests/unit/utils_test.py::ExcludePathsTest::test_directory_with_single_exception tests/unit/utils_test.py::ExcludePathsTest::test_directory_with_subdir_exception tests/unit/utils_test.py::ExcludePathsTest::test_directory_with_trailing_slash tests/unit/utils_test.py::ExcludePathsTest::test_directory_with_wildcard_exception tests/unit/utils_test.py::ExcludePathsTest::test_exclude_custom_dockerfile tests/unit/utils_test.py::ExcludePathsTest::test_exclude_dockerfile_dockerignore tests/unit/utils_test.py::ExcludePathsTest::test_no_dupes tests/unit/utils_test.py::ExcludePathsTest::test_no_excludes tests/unit/utils_test.py::ExcludePathsTest::test_question_mark tests/unit/utils_test.py::ExcludePathsTest::test_single_filename tests/unit/utils_test.py::ExcludePathsTest::test_single_filename_trailing_slash tests/unit/utils_test.py::ExcludePathsTest::test_single_subdir_single_filename tests/unit/utils_test.py::ExcludePathsTest::test_single_subdir_wildcard_filename tests/unit/utils_test.py::ExcludePathsTest::test_subdirectory tests/unit/utils_test.py::ExcludePathsTest::test_wildcard_exclude tests/unit/utils_test.py::ExcludePathsTest::test_wildcard_filename_end tests/unit/utils_test.py::ExcludePathsTest::test_wildcard_filename_start tests/unit/utils_test.py::ExcludePathsTest::test_wildcard_subdir_single_filename tests/unit/utils_test.py::ExcludePathsTest::test_wildcard_subdir_wildcard_filename tests/unit/utils_test.py::ExcludePathsTest::test_wildcard_with_exception tests/unit/utils_test.py::ExcludePathsTest::test_wildcard_with_wildcard_exception tests/unit/utils_test.py::TarTest::test_tar_with_directory_symlinks tests/unit/utils_test.py::TarTest::test_tar_with_empty_directory tests/unit/utils_test.py::TarTest::test_tar_with_excludes tests/unit/utils_test.py::TarTest::test_tar_with_file_symlinks
docker/auth/auth.py docker/utils/utils.py docker/api/image.py
docker/auth/auth.py docker/utils/utils.py docker/api/image.py