Хранение произвольных патчей git, созданных на Python, в файле внутри контейнера Docker с помощью Python SDK.Python

Программы на Python
Ответить
Anonymous
 Хранение произвольных патчей git, созданных на Python, в файле внутри контейнера Docker с помощью Python SDK.

Сообщение Anonymous »

Я извлекаю разницу из Docker-контейнера через стандартный вывод. Затем я собираюсь выполнить некоторые операции над этим различием, а именно выбрать из него некоторый набор фрагментов и построить из них файл патча.
Патч генерируется на Python, а затем я хотел бы сохранить эти данные в файлеchanges.patch внутри Docker-контейнера, чтобы я мог впоследствии применить патч, зафиксировать изменения и т. д. Вариант использования этого заключается в том, чтобы AI-агент автоматически выбирал фрагменты и итеративно обрабатывал их в фиксации. Из-за этого я, если я что-то не пропустил, не могу использовать встроенные интерактивные команды git (в данном случае git add -e), поскольку они зависают на терминале - вот и этот обходной путь.Чтобы заполнить файл патча сгенерированными различиями, я неоднократно натыкался на Here-Documents [1][2][3], с помощью которого я сейчас пытаюсь это реализовать.
Соответствующая часть моего кода выглядит так следует:

Код: Выделить всё

extracted_data = ''.join([diff_header] + selected_hunks)
command = '/bin/bash -c "{command}"'  # Need to quote command for it to actually be executed in the container
update_patch_file_command = command.format(command=f"cat > {file  all_changes.patch  {
+                if (javaType is ParameterizedType && Optional::class.isAssignableFrom(javaType.rawType)) {
+                    // parse and wrap with optional
+                    return Optional.ofNullable(parseInput(value, type, javaType.actualTypeArguments[0], environment))
+                }
+
+                when (val graphQLType = environment.graphQLSchema?.getType(type.name)) {
+                    is GraphQLInputObjectType -> {
+                        if (value == null) return value
+                        return if ((javaType as Class).constructors.any { it.parameters.isEmpty() }) {
+                            parseInputObjectWithNoArgsConstructor(javaType, graphQLType, value, environment)
+                        } else {
+                            parseInputObjectWithAllArgsConstructor(javaType, graphQLType, value, environment)
+                        }
+                    }
+                    is GraphQLScalarType -> when {
+                        type.name == "ID" -> parseIdInput(value, javaType)
+                        javaType is Class && javaType.isPrimitive && value == null -> getPrimitiveDefault(javaType)
+                        else -> value
+                    }
+                    is GraphQLEnumType -> when (value) {
+                        is String -> (javaType as Class)
+                            .getMethod("valueOf", String::class.java)
+                            .invoke(null, value)
+                        else -> value
+                    }
+                    else -> value
+                }
+            }
+            is ListType -> when {
+                value == null -> value
+                javaType is ParameterizedType && Optional::class.isAssignableFrom(javaType.rawType) -> {
+                    // parse and wrap with optional
+                    Optional.ofNullable(parseInput(value, type, javaType.actualTypeArguments[0], environment))
+                }
+                javaType is ParameterizedType && Collection::class.isAssignableFrom(javaType.rawType) ->  {
+                    val collection = (value as Collection).map {
+                        parseInput(it, type.type, javaType.actualTypeArguments[0], environment)
+                    }
+
+                    return when {
+                        List::class.isAssignableFrom(javaType.rawType) -> collection.toList()
+                        Set::class.isAssignableFrom(javaType.rawType) -> collection.toSet()
+                        else -> collection
+                    }
+                }
+                javaType is WildcardType -> parseInput(value, type, javaType.upperBounds[0], environment)
+                else -> value
+            }
+            else -> value
+        }
+    }
+
+    private fun parseInputObjectWithAllArgsConstructor(
+        javaType: Class,
+        graphQLType: GraphQLInputObjectType,
+        value: Any?,
+        environment: DataFetchingEnvironment
+    ): Any? {
+        val fields = parseInputObjectFields(javaType, graphQLType, value, environment)
+
+        return javaType
+            .getDeclaredConstructor(*fields.map { it.first.type }.toTypedArray())
+            .newInstance(*fields.map { it.second }.toTypedArray())
}

-    /**
-     * A concrete scalar type is a scalar type where values always coerce to the same Java type. The ID scalar type is not concrete
-     * because values can be coerced to multiple different Java types (eg. String, Long, UUID). All values of a non-concrete scalar
-     * type must be converted to the target method parameter type.
-     */
-    private fun isConcreteScalarType(environment: DataFetchingEnvironment, type: Type, genericParameterType: JavaType): Boolean {
-        return when (type) {
-            is ListType -> List::class.java.isAssignableFrom(this.genericType.getRawClass(genericParameterType))
-                && isConcreteScalarType(environment, type.type, this.genericType.unwrapGenericType(genericParameterType))
-            is TypeName -> environment.graphQLSchema?.getType(type.name)?.let { isScalar(it) && type.name != "ID" }
-                ?: false
-            is NonNullType -> isConcreteScalarType(environment, type.type, genericParameterType)
-            else -> false
+    private fun parseInputObjectWithNoArgsConstructor(
+        javaType: Class,
+        graphQLType: GraphQLInputObjectType,
+        value: Any?,
+        environment: DataFetchingEnvironment
+    ): Any? {
+        val inputObject = javaType.getDeclaredConstructor().newInstance()
+
+        parseInputObjectFields(javaType, graphQLType, value, environment)
+            .forEach {
+                val field = it.first
+                field.isAccessible = true
+                field.set(inputObject, it.second)
+            }
+
+        return inputObject
+    }
+
+    private fun parseInputObjectFields(
+        javaType: Class,
+        graphQLType: GraphQLInputObjectType,
+        value: Any?,
+        environment: DataFetchingEnvironment
+    ): List
> {
+        return javaType.declaredFields
+            .filterNot { it.isSynthetic }
+            // TODO use an annotation specific to graphql (i.e.  GraphQLIgnore?)
+            .filterNot { it.isAnnotationPresent(JsonIgnore::class.java) }
+            .map {
+                val graphQLField = graphQLType.fields.find { t -> t.definition.name == it.name }
+                    ?: throw IllegalArgumentException("Could not construct input object: missing field '${it.name}' in '${graphQLType.name}' ")
+                val fieldValue = (value as Map)[graphQLField.definition.name]
+                val parsedValue = parseInput(fieldValue, graphQLField.definition.type, it.genericType, environment)
+                Pair(it, parsedValue)
+            }
+    }
+
+    private fun parseIdInput(value: Any?, javaType: JavaType) = when {
+        value !is String // if value was already coerced
+            || String::class.isAssignableFrom(javaType) -> value
+        Int::class.isAssignableFrom(javaType)
+            || Integer::class.isAssignableFrom(javaType) -> Integer.parseInt(value)
+        Long::class.isAssignableFrom(javaType)
+            || java.lang.Long::class.isAssignableFrom(javaType) -> java.lang.Long.parseLong(value)
+        UUID::class.isAssignableFrom(javaType) -> UUID.fromString(value)
+        else -> value
+    }
+
+    private fun KClass.isAssignableFrom(javaType: JavaType): Boolean {
+        return this.java.isAssignableFrom(javaType.unwrap())
+    }
+
+    private fun getPrimitiveDefault(javaType: Class): Any? {
+        return when (javaType) {
+            Boolean::class.java -> false
+            Byte::class.java -> 0
+            Char::class.java -> '\u0000'
+            Int::class.java -> 0
+            Short::class.java -> 0
+            Long::class.java -> 0L
+            Float::class.java -> 0.0f
+            Double::class.java -> 0.0
+            else -> null
}
}

EOL"
Однако контейнер.run_exec возвращает b"not: строка 129: предупреждение: здесь-документ в строке 1 ограничен концом файла (требуется EOL')\n " для этой команды.
Как видно выше, перед EOL нет пробелов (они копируются непосредственно из отладчика), на что может намекать эта ошибка at.
Я пробовал еще следующее:
  • : не работает, потому что мои строки многострочные
  • не двойные кавычки в вызове bash: не работает, потому что тогда команда вообще не выполняется
  • переход к файлу, который еще не существует
  • с использованием повышенных разрешений для контейнера.run_exec
  • используя >|, чтобы принудительно перезаписать файл
  • использование shlex.quote: та же ошибка b"not: строка 129: предупреждение: здесь документ в строке 1 ограничен концом файла (требуется EOL')\n" . Кроме того, насколько мне известно, я считаю, что использовать его безопасно.
  • используя тройник вместо кота
Другие идеи:
Я мог бы скопировать файл с помощью функции Python Docker SDK put_archive, но это кажется очень хакерским и, вероятно, плохо масштабируется. Поэтому я хотел бы найти другое решение.
РЕДАКТИРОВАТЬ 1:
Кроме того, я только что понял, что могу выполнить всю логику извлечения фрагментов в оболочке. скрипт, возможно, полностью избегает взаимодействия с docker/python.

Подробнее здесь: https://stackoverflow.com/questions/793 ... ocker-cont
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»